React组件是独立、可复用的代码块,用于构建用户界面。组件接收输入(props),并返回描述页面展示内容的React元素。
组件可以组合、嵌套,形成复杂的用户界面
使用JavaScript函数定义的组件,是React 16.8之后推荐的主要组件形式。
// 简单的函数组件
function Welcome(props) {
return <h1>Hello, {props.name}!</h1>
}
// 箭头函数形式
const Button = ({ label, onClick }) => {
return (
<button onClick={onClick} className="btn">
{label}
</button>
);
}
// 使用组件
const App = () => {
return (
<div>
<Welcome name="React开发者" />
<Button label="点击我" onClick={() => alert('按钮被点击!')} />
</div>
);
}
使用ES6类定义的组件,支持生命周期方法和状态(state)。
import React from 'react';
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}!</h1>
}
}
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
增加
</button>
</div>
);
}
}
Props是组件之间传递数据的机制,它是只读的(immutable)。
数据只能从父组件流向子组件(单向数据流)
// 父组件传递数据
function App() {
const user = {
name: '张三',
age: 28,
job: '前端工程师'
};
return (
<div>
<UserProfile
name={user.name}
age={user.age}
job={user.job}
isAdmin={true}
onUpdate={(newName) => console.log('更新:', newName)}
/>
</div>
);
}
// 子组件接收Props
function UserProfile(props) {
return (
<div className="user-profile">
<h2>用户信息</h2>
<p>姓名: {props.name}</p>
<p>年龄: {props.age}</p>
<p>职业: {props.job}</p>
{props.isAdmin && <span className="badge bg-danger">管理员</span>}
<button onClick={() => props.onUpdate(props.name + ' (已更新)')}>
更新信息
</button>
</div>
);
}
使用ES6解构语法让代码更简洁:
// 方式1:在参数中解构
function UserCard({ name, age, email, isVerified }) {
return (
<div className="user-card">
<h3>{name}</h3>
<p>年龄: {age}</p>
<p>邮箱: {email}</p>
{isVerified && <span className="verified">✓ 已认证</span>}
</div>
);
}
// 方式2:在函数体中解构
function UserCard(props) {
const { name, age, email, isVerified } = props;
return (
<div className="user-card">
<h3>{name}</h3>
<p>年龄: {age}</p>
<p>邮箱: {email}</p>
{isVerified && <span className="verified">✓ 已认证</span>}
</div>
);
}
// 方式1:使用默认参数值
function Greeting({ name = '访客', greeting = '你好' }) {
return <h1>{greeting}, {name}!</h1>
}
// 方式2:使用defaultProps属性
function Button({ text, color, size }) {
return (
<button className={`btn btn-${color} btn-${size}`}>
{text}
</button>
);
}
Button.defaultProps = {
text: '按钮',
color: 'primary',
size: 'md'
};
// 类组件的defaultProps
class Alert extends React.Component {
static defaultProps = {
type: 'info',
message: '默认消息'
};
render() {
return (
<div className={`alert alert-${this.props.type}`}>
{this.props.message}
</div>
);
}
}
children是一个特殊的prop,用于传递组件标签之间的内容。
// 容器组件示例
function Card({ title, children }) {
return (
<div className="card">
{title && <div className="card-header">{title}</div>}
<div className="card-body">
{children}
</div>
</div>
);
}
// 使用Card组件
function App() {
return (
<Card title="用户信息">
<p>姓名: 李四</p>
<p>年龄: 25</p>
<p>邮箱: lisi@example.com</p>
<button>编辑信息</button>
</Card>
);
}
// 渲染函数作为children
function DataFetcher({ url, children }) {
const [data, setData] = React.useState(null);
const [loading, setLoading] = React.useState(true);
React.useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => {
setData(data);
setLoading(false);
});
}, [url]);
// 将children作为函数调用
return children({ data, loading });
}
// 使用方式
function App() {
return (
<DataFetcher url="https://api.example.com/user">
{({ data, loading }) => (
<div>
{loading ? (
<p>加载中...</p>
) : (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)}
</div>
)}
</DataFetcher>
);
}
使用PropTypes为组件props添加类型检查(需要单独安装prop-types库)。
import PropTypes from 'prop-types';
function Product({
name,
price,
isAvailable,
categories,
onBuy
}) {
return (
<div className="product">
<h3>{name}</h3>
<p>价格: ¥{price}</p>
<p>状态: {isAvailable ? '有货' : '缺货'}</p>
<div>
分类: {categories.join(', ')}
</div>
<button onClick={onBuy} disabled={!isAvailable}>
购买
</button>
</div>
);
}
// 定义PropTypes
Product.propTypes = {
name: PropTypes.string.isRequired,
price: PropTypes.number.isRequired,
isAvailable: PropTypes.bool,
categories: PropTypes.arrayOf(PropTypes.string),
onBuy: PropTypes.func
};
// 默认值
Product.defaultProps = {
isAvailable: true,
categories: []
};
| PropTypes类型 | 说明 | 示例 |
|---|---|---|
PropTypes.string |
字符串类型 | name: PropTypes.string |
PropTypes.number |
数字类型 | age: PropTypes.number |
PropTypes.bool |
布尔类型 | isActive: PropTypes.bool |
PropTypes.array |
数组类型 | items: PropTypes.array |
PropTypes.object |
对象类型 | user: PropTypes.object |
PropTypes.func |
函数类型 | onClick: PropTypes.func |
PropTypes.element |
React元素 | icon: PropTypes.element |
.isRequired |
必需属性 | title: PropTypes.string.isRequired |
// 基础组件
function Button({ children, onClick, variant = 'primary' }) {
return (
<button className={`btn btn-${variant}`} onClick={onClick}>
{children}
</button>
);
}
function Card({ children, title }) {
return (
<div className="card">
{title && <div className="card-title">{title}</div>}
<div className="card-content">{children}</div>
</div>
);
}
// 复合组件
function UserDashboard() {
const users = [
{ id: 1, name: '张三', role: '管理员' },
{ id: 2, name: '李四', role: '编辑' },
{ id: 3, name: '王五', role: '用户' }
];
return (
<div className="dashboard">
<Card title="用户管理">
<div className="user-list">
{users.map(user => (
<div key={user.id} className="user-item">
<span>{user.name}</span>
<span className="role-badge">{user.role}</span>
<Button variant="outline" onClick={() => console.log('编辑', user.id)}>
编辑
</Button>
</div>
))}
</div>
<div className="dashboard-actions">
<Button variant="success" onClick={() => console.log('添加用户')}>
添加用户
</Button>
<Button variant="danger" onClick={() => console.log('批量删除')}>
批量删除
</Button>
</div>
</Card>
</div>
);
}
onEvent格式
import PropTypes from 'prop-types';
function ProductItem({
product,
onAddToCart,
onViewDetail
}) {
const {
id,
name,
price,
description,
image,
stock,
category
} = product;
return (
<div className="product-item">
<div className="product-image">
<img src={image} alt={name} />
{stock < 5 && <span className="stock-warning">仅剩{stock}件</span>}
</div>
<div className="product-info">
<h3 className="product-name">{name}</h3>
<p className="product-description">{description}</p>
<div className="product-meta">
<span className="product-price">¥{price.toFixed(2)}</span>
<span className="product-category">{category}</span>
</div>
<div className="product-actions">
<button
className="btn btn-primary"
onClick={() => onAddToCart(id)}
disabled={stock === 0}
>
{stock === 0 ? '已售罄' : '加入购物车'}
</button>
<button
className="btn btn-outline-secondary"
onClick={() => onViewDetail(id)}
>
查看详情
</button>
</div>
</div>
</div>
);
}
// PropTypes定义
ProductItem.propTypes = {
product: PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
price: PropTypes.number.isRequired,
description: PropTypes.string,
image: PropTypes.string.isRequired,
stock: PropTypes.number.isRequired,
category: PropTypes.string.isRequired
}).isRequired,
onAddToCart: PropTypes.func.isRequired,
onViewDetail: PropTypes.func.isRequired
};
// 使用ProductItem的父组件
function ProductList({ products, onAddToCart, onViewDetail }) {
return (
<div className="product-list">
{products.length === 0 ? (
<p className="empty-message">暂无商品</p>
) : (
<div className="products-grid">
{products.map(product => (
<ProductItem
key={product.id}
product={product}
onAddToCart={onAddToCart}
onViewDetail={onViewDetail}
/>
))}
</div>
)}
</div>
);
}
children prop用于传递组件内容