React 组件使用props进行相互通信。每个父组件都可以通过向其子组件传递 props 来传递一些信息。Props 可能会让你想起 HTML 属性,但是你可以通过它们传递任何 JavaScript 值,包括对象、数组和函数。
你将学习
- 如何向组件传递 props
- 如何从组件中读取 props
- 如何为 props 指定默认值
- 如何向组件传递一些 JSX
- props 如何随时间变化
熟悉的 props
Props 是你传递给 JSX 标签的信息。例如,className
、src
、alt
、width
和 height
是你可以传递给 <img>
的一些 props。
function Avatar() { return ( <img className="avatar" src="https://i.imgur.com/1bX5QH6.jpg" alt="Lin Lanying" width={100} height={100} /> ); } export default function Profile() { return ( <Avatar /> ); }
你可以传递给 <img>
标签的 props 是预定义的(ReactDOM 符合HTML 标准)。但是你可以向你自己的组件(例如 <Avatar>
)传递任何 props 来自定义它们。方法如下!
向组件传递 props
在此代码中,Profile
组件没有向其子组件 Avatar
传递任何 props。
export default function Profile() {
return (
<Avatar />
);
}
你可以分两个步骤向 Avatar
传递一些 props。
步骤 1:向子组件传递 props
首先,向 Avatar
传递一些 props。例如,让我们传递两个 props:person
(一个对象)和 size
(一个数字)
export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
size={100}
/>
);
}
现在你可以读取 Avatar
组件内的这些 props。
步骤 2:在子组件内读取 props
你可以在 function Avatar
后面,在 ({
和 })
之间,列出它们的名称 person, size
(用逗号分隔)来读取这些 props。这让你可以在 Avatar
代码中使用它们,就像使用变量一样。
function Avatar({ person, size }) {
// person and size are available here
}
在 Avatar
中添加一些逻辑,使用 person
和 size
属性进行渲染,就完成了。
现在你可以使用不同的属性配置 Avatar
以多种不同的方式进行渲染。尝试调整这些值!
import { getImageUrl } from './utils.js'; function Avatar({ person, size }) { return ( <img className="avatar" src={getImageUrl(person)} alt={person.name} width={size} height={size} /> ); } export default function Profile() { return ( <div> <Avatar size={100} person={{ name: 'Katsuko Saruhashi', imageId: 'YfeOqp2' }} /> <Avatar size={80} person={{ name: 'Aklilu Lemma', imageId: 'OKS67lh' }} /> <Avatar size={50} person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }} /> </div> ); }
属性允许你独立地思考父组件和子组件。例如,你可以在 Profile
内部更改 person
或 size
属性,而无需考虑 Avatar
如何使用它们。类似地,你可以更改 Avatar
如何使用这些属性,而无需查看 Profile
。
你可以将属性视为可以调整的“旋钮”。它们的作用与函数的参数相同——事实上,属性 *是* 组件的唯一参数!React 组件函数接受单个参数,一个 props
对象。
function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}
通常你不需要整个 props
对象本身,因此你可以将其解构为单个属性。
指定属性的默认值
如果要为属性提供一个默认值,以便在未指定值时回退,可以通过解构在参数后添加 =
和默认值来实现。
function Avatar({ person, size = 100 }) {
// ...
}
现在,如果 <Avatar person={...} />
渲染时没有 size
属性,则 size
将设置为 100
。
只有当 size
属性缺失或传递 size={undefined}
时,才会使用默认值。但如果传递 size={null}
或 size={0}
,则 不会 使用默认值。
使用 JSX 展开语法转发属性
有时,传递属性会变得非常重复。
function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}
重复的代码并没有错——它可能更易读。但在某些时候,你可能会重视简洁性。有些组件会将其所有属性转发给其子组件,就像这个 Profile
对 Avatar
所做的那样。因为它们没有直接使用任何属性,所以使用更简洁的“展开”语法是有意义的。
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}
这将 Profile
的所有属性转发到 Avatar
,而无需列出每个属性的名称。
谨慎使用展开语法。如果你在每个组件中都使用它,那就有问题了。通常,这表示你应该拆分组件并将子组件作为 JSX 传递。更多内容将在后面介绍!
将 JSX 作为子组件传递
嵌套内置浏览器标签是很常见的。
<div>
<img />
</div>
有时你可能也希望以同样的方式嵌套你自己的组件。
<Card>
<Avatar />
</Card>
当你在 JSX 标签内嵌套内容时,父组件将在一个名为 children
的属性中接收该内容。例如,下面的 Card
组件将接收一个设置为 <Avatar />
的 children
属性,并在包装 div 中渲染它。
import Avatar from './Avatar.js'; function Card({ children }) { return ( <div className="card"> {children} </div> ); } export default function Profile() { return ( <Card> <Avatar size={100} person={{ name: 'Katsuko Saruhashi', imageId: 'YfeOqp2' }} /> </Card> ); }
尝试用一些文本替换 <Card>
内的 <Avatar>
,看看 Card
组件如何包装任何嵌套内容。它不需要“知道”在其内部渲染的内容。你会在很多地方看到这种灵活的模式。
你可以将具有 children
属性的组件视为具有一个“孔”,该“孔”可以由其父组件用任意 JSX“填充”。你通常会将 children
属性用于可视化包装器:面板、网格等。
export default function Clock({ color, time }) { return ( <h1 style={{ color: color }}> {time} </h1> ); }
此示例说明组件可能会随时间接收不同的属性。属性并非总是静态的!在这里,time
属性每秒钟变化一次,而当您选择另一种颜色时,color
属性就会发生变化。属性反映了组件在任何时间点的数剧,而不仅仅是在开始时。
但是,属性是不可变的——这是计算机科学中的一个术语,意思是“不可更改的”。当组件需要更改其属性(例如,响应用户交互或新数据)时,它必须“请求”其父组件传递不同的属性——一个新的对象!然后,它的旧属性将被丢弃,最终 JavaScript 引擎将回收它们占用的内存。
不要尝试“更改属性”。当您需要响应用户输入(例如更改所选颜色)时,您需要“设置状态”,您可以在状态:组件的内存中学习。
回顾
- 要传递属性,请将其添加到 JSX 中,就像使用 HTML 属性一样。
- 要读取属性,请使用
function Avatar({ person, size })
解构语法。 - 您可以指定一个默认值,例如
size = 100
,它用于缺失和undefined
属性。 - 您可以使用
<Avatar {...props} />
JSX 展开语法转发所有属性,但不要过度使用它! - 像
<Card><Avatar /></Card>
这样的嵌套 JSX 将显示为Card
组件的children
属性。 - 属性是时间上的只读快照:每次渲染都会收到一个新的属性版本。
- 您不能更改属性。当您需要交互性时,您需要设置状态。
挑战 1的 3: 提取组件
此Gallery
组件包含两个配置文件中一些非常相似的标记。从中提取一个Profile
组件以减少重复。您需要选择要传递给它的属性。
import { getImageUrl } from './utils.js'; export default function Gallery() { return ( <div> <h1>Notable Scientists</h1> <section className="profile"> <h2>Maria Skłodowska-Curie</h2> <img className="avatar" src={getImageUrl('szV5sdG')} alt="Maria Skłodowska-Curie" width={70} height={70} /> <ul> <li> <b>Profession: </b> physicist and chemist </li> <li> <b>Awards: 4 </b> (Nobel Prize in Physics, Nobel Prize in Chemistry, Davy Medal, Matteucci Medal) </li> <li> <b>Discovered: </b> polonium (chemical element) </li> </ul> </section> <section className="profile"> <h2>Katsuko Saruhashi</h2> <img className="avatar" src={getImageUrl('YfeOqp2')} alt="Katsuko Saruhashi" width={70} height={70} /> <ul> <li> <b>Profession: </b> geochemist </li> <li> <b>Awards: 2 </b> (Miyake Prize for geochemistry, Tanaka Prize) </li> <li> <b>Discovered: </b> a method for measuring carbon dioxide in seawater </li> </ul> </section> </div> ); }