本文译自官方文档:Passing Props to a Component
React 组件通过属性(props)实现相互通信。每个父组件可以向其子组件的 props 传递一些信息。props 可能会让你联想到 HTML 的属性(attributes),但是你可以向 props 传递任意数据类型,包括对象、数组和函数。
你会学到
- 如何向组件传递 props
- 如何从组件中读取 props
- 如何指定 props 默认值
- 如何向组件传递 JSX
- props 如何随时间变化
常见的 props
props 是向 JSX 标签传递的信息。比如 className, src, alt, width, height 是一些 <img> 支持的 props:
function Avatar() {
return (
<img
className="avatar"
src="xxx.jpg"
alt="Lin Lanying"
width={100}
height={100}
/>
);
}
可以向 <img> 标签传递的 props 是预定义的(ReactDOM 遵循 HTML 标准)。但是,你可以向自定义组件(比如 <Avatar />)传递任意属性。
向组件传递 props
以自定义组件 Avatar 为例,向它传递 props 分两步走
第一步:向子组件传递 props
首先,向 Avatar 传递一些 props。比如,我们在此传递两个 props:person(对象) 和 size(数字):
export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: 'xxx' }}
size={100}
/>
);
}
此时,可以在 Avatar 组件内部读取这些 props。
第二步:在子组件内部读取 props
function Avatar({ person, size }) {
return (
<img
className="avatar"
src={getImageUrl(person)}
alt={person.name}
width={size}
height={size}
/>
);
}
指定 props 的默认值
使用解构语法的 = 可以设定参数默认值:
function Avatar({ person, size = 100 }) {
// ...
}
使用 JSX 扩展语法传递 props
有时候,传递 props 时非常重复:
function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}
重复性代码本身没错 --- 它的意图更明确。但有时也许你更偏爱简洁。一些组件会把全部 props 转发给子组件,就像上面 Profile 对 Avatar 做的那样。因为它自身不会直接使用 props,使用更紧凑的“扩展”语法也是合理的:
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}
使用扩展语法时要克制。如果你几乎每个组件都使用,很可能用错了方法。通常,这意味着你需要拆分组件,把子组件当作 JSX 传递。后文会详解。
将 JSX 当作 children 传递
在浏览器原生标签中经常使用嵌套用法:
<div>
<img />
</div>
有时候,你也期望可以像这样嵌套自己的组件:
<Card>
<Avatar />
</Card>
当你在 JSX 标签中嵌套内容时,父组件接收的内容,将放在一个名叫 children 的 prop 中。比如,下面的 Card 组件接收的 children prop 将设为 <Avatar /> 并将它包裹在 div 中渲染:
function Card({ children }) {
return (
<div className="card">
{children}
</div>
);
}
试着将 <Card> 内部的 <Avatar> 替换为一些文本,可以看到 Card 组件可以包裹任意类型的内容。它无需在意内部渲染的元素。许多场景可以看到这种灵活模式。
自带 children prop 的组件可以想象成有一个“洞”,它的父组件可以使用 JSX 填充这个洞。children prop 组件通常用作视觉包裹容器:比如面板、表格等等。可以在提取布局元素中看到更多应用。
props 如何随时间变化
下面的 Clock 组件从父组件接收两个 props:color 和 time。(暂时忽略父组件的代码,因为它使用了 state,先不深究 state 的用法)。
export default function Clock({ color, time }) {
return (
<h1 style={{ color: color }}>
{time}
</h1>
);
}
这个例子展示了组件可能在不同时刻接收不同的 props。props 不是静止的!这里的 time prop 每秒变化一次,color prop 随着下拉菜单的变更而变化。props 反映了任意时刻组件的数据,并非仅仅初始值。
但是,props 是不可变的 --- 这是一个计算机科学的术语,表示“不能更改”。当组件需要改变自己的 props(比如,为了应对用户交互或新的数据),它需要告知父组件传递不同的 props --- 一个新的对象!旧 props 会搁置一旁,JS 引擎最终回收旧 props 占用的内存空间。
总结
- 传递 props 时,添加到 JSX 上即可,就像 HTML 属性一样。
- 读取 props 需要使用
function Avatar({ person, size })解构语法。 - 可以指定默认值,比如
size = 100。 - 可以使用 JSX 扩展语法
<Avatar {...props} />转发所有的 props,但不要过量! - 嵌套的 JSX
<Card><Avatar /></Card>会出现在Card组件的childrenprop。 - props 是只读的时间快照:每次渲染都会拥有新的 props。
- 你不能改变 props。当需要交互时,需要设定 state。