将属性传递给组件

React 组件使用属性相互通信。每个父组件都可以通过给它们属性将一些信息传递给它的子组件。属性可能会让你想起 HTML 属性,但你可以通过它们传递任何 JavaScript 值,包括对象、数组和函数。

¥React components use props to communicate with each other. Every parent component can pass some information to its child components by giving them props. Props might remind you of HTML attributes, but you can pass any JavaScript value through them, including objects, arrays, and functions.

你将学习到

  • 如何将属性传递给组件

    ¥How to pass props to a component

  • 如何从组件中读取属性

    ¥How to read props from a component

  • 如何为属性指定默认值

    ¥How to specify default values for props

  • 如何将一些 JSX 传递给组件

    ¥How to pass some JSX to a component

  • 属性如何随时间变化

    ¥How props change over time

熟悉的属性

¥Familiar props

属性是你传递给 JSX 标签的信息。例如,classNamesrcaltwidthheight 是你可以传递给 <img> 的一些属性:

¥Props are the information that you pass to a JSX tag. For example, className, src, alt, width, and height are some of the props you can pass to an <img>:

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> 标签的属性是预定义的(ReactDOM 符合 HTML 标准)。但是你可以将任何属性传递给你自己的组件,例如 <Avatar>,以自定义它们。就是这样!

¥The props you can pass to an <img> tag are predefined (ReactDOM conforms to the HTML standard). But you can pass any props to your own components, such as <Avatar>, to customize them. Here’s how!

将属性传递给组件

¥Passing props to a component

在这段代码中,Profile 组件没有将任何属性传递给它的子组件 Avatar

¥In this code, the Profile component isn’t passing any props to its child component, Avatar:

export default function Profile() {
return (
<Avatar />
);
}

你可以分两步给 Avatar 一些属性。

¥You can give Avatar some props in two steps.

步骤 1:将属性传递给子组件

¥Step 1: Pass props to the child component

首先,将一些属性传递给 Avatar。例如,让我们传递两个属性:person(一个对象)和 size(一个数字):

¥First, pass some props to Avatar. For example, let’s pass two props: person (an object), and size (a number):

export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
size={100}
/>
);
}

注意

如果 person= 之后的双大括号让你感到困惑,请回忆一下 JSX 大括号内的 它们只是一个对象

¥If double curly braces after person= confuse you, recall they’re merely an object inside the JSX curlies.

现在你可以在 Avatar 组件中读取这些属性。

¥Now you can read these props inside the Avatar component.

步骤 2:读取子组件内的属性

¥Step 2: Read props inside the child component

你可以直接在 function Avatar 之后通过在 ({}) 中列出它们的名称 person, size 并用逗号分隔来读取这些属性。这使你可以在 Avatar 代码中使用它们,就像使用变量一样。

¥You can read these props by listing their names person, size separated by the commas inside ({ and }) directly after function Avatar. This lets you use them inside the Avatar code, like you would with a variable.

function Avatar({ person, size }) {
// person and size are available here
}

Avatar 添加一些使用 personsize 属性进行渲染的逻辑,就完成了。

¥Add some logic to Avatar that uses the person and size props for rendering, and you’re done.

现在你可以将 Avatar 配置为使用不同的属性以多种不同的方式渲染。尝试调整值!

¥Now you can configure Avatar to render in many different ways with different props. Try tweaking the values!

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 中更改 personsize 属性,而不必考虑 Avatar 如何使用它们。同样,你可以更改 Avatar 使用这些属性的方式,而无需查看 Profile

¥Props let you think about parent and child components independently. For example, you can change the person or the size props inside Profile without having to think about how Avatar uses them. Similarly, you can change how the Avatar uses these props, without looking at the Profile.

你可以想像 “knobs” 这样的属性,你可以调整。它们的作用与函数参数的作用相同 - 事实上,props 是组件的唯一参数!React 组件函数接受一个参数,一个 props 对象:

¥You can think of props like “knobs” that you can adjust. They serve the same role as arguments serve for functions—in fact, props are the only argument to your component! React component functions accept a single argument, a props object:

function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}

通常你不需要整个 props 对象本身,所以你将它解构为单独的属性。

¥Usually you don’t need the whole props object itself, so you destructure it into individual props.

易犯错误

声明属性时,不要遗漏 () 内的一对 {} 花句:

¥Don’t miss the pair of { and } curlies inside of ( and ) when declaring props:

function Avatar({ person, size }) {
// ...
}

此语法称为 “解构”,等效于从函数参数读取属性:

¥This syntax is called “destructuring” and is equivalent to reading properties from a function parameter:

function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}

为属性指定默认值

¥Specifying a default value for a prop

如果你想给一个属性一个默认值以在没有指定值时返回,你可以通过将 = 和默认值放在参数后面来使用解构来实现:

¥If you want to give a prop a default value to fall back on when no value is specified, you can do it with the destructuring by putting = and the default value right after the parameter:

function Avatar({ person, size = 100 }) {
// ...
}

现在,如果在没有 size 属性的情况下渲染 <Avatar person={...} />,则 size 将设置为 100

¥Now, if <Avatar person={...} /> is rendered with no size prop, the size will be set to 100.

默认值仅在缺少 size 属性或传递 size={undefined} 时使用。但如果传递 size={null}size={0},则不会使用默认值。

¥The default value is only used if the size prop is missing or if you pass size={undefined}. But if you pass size={null} or size={0}, the default value will not be used.

使用 JSX 展开语法转发属性

¥Forwarding props with the JSX spread syntax

有时,传递属性会变得非常重复:

¥Sometimes, passing props gets very repetitive:

function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}

重复的代码没有什么问题 - 它可以更清晰。但有时你可能会看重简洁。一些组件将它们所有的属性转发给它们的子级,就像这个 ProfileAvatar 的处理方式。因为它们不直接使用任何属性,所以使用更简洁的 “展开” 语法是有意义的:

¥There’s nothing wrong with repetitive code—it can be more legible. But at times you may value conciseness. Some components forward all of their props to their children, like how this Profile does with Avatar. Because they don’t use any of their props directly, it can make sense to use a more concise “spread” syntax:

function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}

这会将 Profile 的所有属性转发给 Avatar,但不会列出每个属性的名称。

¥This forwards all of Profile’s props to the Avatar without listing each of their names.

有节制地使用展开语法。如果你在所有其他组件中都使用它,那就有问题了。通常,它表示你应该拆分组件并将子级作为 JSX 传递。接下来会详细介绍!

¥Use spread syntax with restraint. If you’re using it in every other component, something is wrong. Often, it indicates that you should split your components and pass children as JSX. More on that next!

作为子级传递 JSX

¥Passing JSX as children

嵌套内置浏览器标签很常见:

¥It is common to nest built-in browser tags:

<div>
<img />
</div>

有时你会希望以相同的方式嵌套自己的组件:

¥Sometimes you’ll want to nest your own components the same way:

<Card>
<Avatar />
</Card>

当你将内容嵌套在 JSX 标签中时,父组件将在名为 children 的属性中接收该内容。例如,下面的 Card 组件将收到一个设置为 <Avatar />children 属性并将其渲染在封装器 div 中:

¥When you nest content inside a JSX tag, the parent component will receive that content in a prop called children. For example, the Card component below will receive a children prop set to <Avatar /> and render it in a wrapper 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 组件如何封装任何嵌套内容。它不需要 “知道” 在其中渲染的内容。你会在很多地方看到这种灵活的模式。

¥Try replacing the <Avatar> inside <Card> with some text to see how the Card component can wrap any nested content. It doesn’t need to “know” what’s being rendered inside of it. You will see this flexible pattern in many places.

你可以将具有 children 属性的组件视为具有 “空缺”,而 “空缺” 可以被其具有任意 JSX 的父组件 “填补”。你将经常使用 children 属性作为可视封装器:面板、网格等

¥You can think of a component with a children prop as having a “hole” that can be “filled in” by its parent components with arbitrary JSX. You will often use the children prop for visual wrappers: panels, grids, etc.

A puzzle-like Card tile with a slot for "children" pieces like text and Avatar

Illustrated by Rachel Lee Nabors

属性如何随时间变化

¥How props change over time

下面的 Clock 组件从其父组件接收两个属性:colortime。(父组件的代码被省略,因为它使用了 状态,我们暂时不深入探讨。)

¥The Clock component below receives two props from its parent component: color and time. (The parent component’s code is omitted because it uses state, which we won’t dive into just yet.)

尝试在下面的选择框中更改颜色:

¥Try changing the color in the select box below:

export default function Clock({ color, time }) {
  return (
    <h1 style={{ color: color }}>
      {time}
    </h1>
  );
}

此示例说明组件可能会随着时间的推移接收不同的属性。属性并不总是静态的!在这里,time 属性每秒都在变化,当你选择另一种颜色时,color 属性也会变化。属性反映组件在任何时间点的数据,而不仅仅是在开始时。

¥This example illustrates that a component may receive different props over time. Props are not always static! Here, the time prop changes every second, and the color prop changes when you select another color. Props reflect a component’s data at any point in time, rather than only in the beginning.

然而,props 是 不可变的 - 计算机科学中的一个术语,意思是 “不可修改的”。当组件需要更改其属性时(例如,响应用户交互或新数据),它必须对其父组件进行 “询问” 才能向其传递不同的属性- 一个新对象!它的旧属性将被丢弃,最终 JavaScript 引擎将回收它们占用的内存。

¥However, props are immutable—a term from computer science meaning “unchangeable”. When a component needs to change its props (for example, in response to a user interaction or new data), it will have to “ask” its parent component to pass it different props—a new object! Its old props will then be cast aside, and eventually the JavaScript engine will reclaim the memory taken by them.

不要尝试 “改变属性”。当你需要响应用户输入时(比如改变选择的颜色),你将需要 “设置状态”,你可以在 状态:组件的内存。 中了解。

¥Don’t try to “change props”. When you need to respond to the user input (like changing the selected color), you will need to “set state”, which you can learn about in State: A Component’s Memory.

回顾

  • 要传递属性,将它们添加到 JSX,就像使用 HTML 属性一样。

    ¥To pass props, add them to the JSX, just like you would with HTML attributes.

  • 要读取属性,请使用 function Avatar({ person, size }) 解构语法。

    ¥To read props, use the function Avatar({ person, size }) destructuring syntax.

  • 你可以指定一个默认值,如 size = 100,用于缺失和 undefined 属性。

    ¥You can specify a default value like size = 100, which is used for missing and undefined props.

  • 你可以使用 <Avatar {...props} /> JSX 展开语法转发所有属性,但不要过度使用它!

    ¥You can forward all props with <Avatar {...props} /> JSX spread syntax, but don’t overuse it!

  • <Card><Avatar /></Card> 这样的嵌套 JSX 将作为 Card 组件的 children 属性出现。

    ¥Nested JSX like <Card><Avatar /></Card> will appear as Card component’s children prop.

  • 属性是时间上的只读快照:每个渲染器都会收到一个新版本的属性。

    ¥Props are read-only snapshots in time: every render receives a new version of props.

  • 你不能改变属性。当你需要交互时,你需要设置状态。

    ¥You can’t change props. When you need interactivity, you’ll need to set state.

挑战 1 / 3:
提取一个组件

¥Extract a component

这个 Gallery 组件包含两个配置文件的一些非常相似的标记。从中提取一个 Profile 组件以减少重复。你需要选择要传递给它的属性。

¥This Gallery component contains some very similar markup for two profiles. Extract a Profile component out of it to reduce the duplication. You’ll need to choose what props to pass to it.

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>
  );
}


React 中文网 - 粤ICP备13048890号