什么是 Props
Props(Properties 的缩写)是 React 组件对外暴露的接口,是父组件向子组件传递数据的唯一方式。你可以把 Props 理解为函数的参数:组件是一个函数,Props 是传入的实参,组件根据这些参数返回对应的 UI。
function Greeting({ name }) {
return <h1>你好,{name}!</h1>;
}
// 使用组件时传递 Props
<Greeting name="张三" />
在这个例子中,name 就是 Greeting 组件的 Props,它的值是 "张三"。组件内部通过解构参数直接使用这个值。
Props 是只读的
React 有一条严格的规则:组件绝不能修改自己的 Props。Props 是“只读”的,类似于纯函数的参数——相同的输入必须产生相同的输出,不能在函数内部篡改传入的值。
// ❌ 错误:尝试修改 Props
function Welcome({ user }) {
user.name = '李四'; // 不允许!
return <div>{user.name}</div>;
}
// ✅ 正确:只读取 Props,不做修改
function Welcome({ user }) {
return <div>{user.name}</div>;
}
之所以设定这个规则,是为了保持单向数据流的纯粹性。如果子组件能随意修改 Props,数据的流向就会变得混乱,整个应用的状态将难以追踪。当你需要根据用户交互改变界面时,应该通过状态(State)或回调函数通知父组件来触发更新,而不是直接修改 Props。
传递任意类型的值
Props 可以传递 JavaScript 中任意类型的值:
- 字符串:
<Button text="提交" /> - 数字:
<Pagination current={1} total={10} /> - 布尔值:
<Modal visible={true} />(省略值时默认为 true:<Modal visible />) - 数组:
<List items={['A', 'B', 'C']} /> - 对象:
<UserCard user={{ name: '张三', age: 25 }} /> - 函数(极其常见):
<Button onClick={handleClick} /> - JSX 元素(作为 children 或自定义 Prop):
<Card header={<h2>标题</h2>} />
// 实际案例:一个弹窗组件
function Modal({ visible, title, children, onClose }) {
if (!visible) return null;
return (
<div className="modal-overlay">
<div className="modal">
<div className="modal-header">
<h2>{title}</h2>
<button onClick={onClose}>关闭</button>
</div>
<div className="modal-body">{children}</div>
</div>
</div>
);
}
// 使用
<Modal
visible={isOpen}
title="用户详情"
onClose={() => setIsOpen(false)}
>
<p>这里是弹窗的具体内容</p>
</Modal>
Props 的默认值
当某些 Props 未传入时,你可能希望组件有一个回退的默认值。可以通过解构时的默认值语法实现:
function Button({ text = '点击', size = 'medium', disabled = false }) {
return (
<button
className={`btn btn-${size}`}
disabled={disabled}
>
{text}
</button>
);
}
// 不传 text 时默认显示"点击"
<Button />;
<Button text="删除" size="small" />;
这种方式比使用 defaultProps 静态属性(类组件时代的写法)更加直观且符合现代 JavaScript 习惯。
使用展开运算符传递 Props
当你有多个 Props 需要传递,或者需要将某个对象的所有属性拆开传入时,可以使用展开运算符:
const userInfo = {
name: '王五',
age: 30,
role: 'admin',
};
<UserCard {...userInfo} />;
// 等价于
<UserCard name="王五" age={30} role="admin" />
这在实际开发中很常见,例如将父组件接收到的所有 Props 透传给子组件:
function PrimaryButton(props) {
return <button className="primary" {...props} />;
}
// 使用
<PrimaryButton onClick={handleClick} disabled={true}>提交</PrimaryButton>
需要注意的是,过度使用展开运算符可能让 Props 的来源和内容变得不清晰,建议在明确需要批量传递且结构稳定的场景下使用。
Props 的命名与设计原则
为了让组件易用且易于维护,Props 设计应遵循以下原则:
- 语义清晰:使用描述性的名称,比如
isLoading而不是简单的flag。 - 最小化暴露:只暴露组件真正需要的数据,避免传递整个大对象。例如,需要用户名时只传
username,而不是整个user对象(除非组件确实需要对象的多个字段)。 - 保持一致的命名习惯:布尔值通常以
is、has、should开头(如isVisible、hasError),事件处理函数以on开头(如onClick、onSubmit)。 - 必要的类型检查:在 TypeScript 中为 Props 定义 interface,在 JavaScript 中可以使用 PropTypes(较少用)提供运行时警告。
// TypeScript 示例
interface UserCardProps {
name: string;
age?: number; // 可选属性
onFollow: (id: string) => void;
}
function UserCard({ name, age, onFollow }: UserCardProps) {
// ...
}
Props 的只读约束与纯函数思维
将 Props 视为只读不仅是规则,更是一种思维方式:把组件当作纯函数。纯函数不会修改输入值,且同样的输入永远得到同样的输出。这种思维让你的组件行为完全可预测:
function sum(a, b) {
return a + b;
}
// sum(1, 2) 永远返回 3,并且不会修改 a 和 b
function UserName({ name }) {
return <span>{name}</span>;
}
// 给定相同的 name,总是渲染相同的 <span>,并且不会修改 name
理解并遵守这一约束后,你的组件将更容易测试、调试和复用,因为任何时刻你都可以根据传入的 Props 准确推断组件的外观和行为,而不必担心内部有什么“小动作”改变了这些数据。