此文章是翻译Components and Props这篇React(版本v16.2.0 )官方文档。

Components and Props

组件让你将组件分拆独立的、可复用的块,并考虑独立的每一个部分。

从概念上讲,组件类似于JavaScript 函数。它们接受任意的输入(被称为“props”)并且返回React 元素描述在屏幕上如何展示。

Functional and Class Components

最简单的定义一个组件的方式是写一个JavaScript 函数:

  1. function Welcome(props) {
  2. return <h1>Hello, {props.name}</h1>;
  3. }

这个函数是一个有效的React 组件因为它接受一个“props”(代表属性(properties))对象参数接受数据并且返回一个React 元素。我们称这样的组件为“函数式(funcitional)”因为它们是字面量JavaScript 函数。

你也可以使用ES6 class 去定义一个组件:

  1. class Welcome extends React.Component {
  2. render(){
  3. return <h1>Hello, {this.props.name}</h1>;
  4. }
  5. }

从React 的View 角度来看上述两个组件是相等的。

类有一些额外的特性,这些特性将在下一章 讨论。在此之前,我们将使用函数式组件来简化。

Rendering a Component

之前,我们只遇到使用React 元素来代表DOM 标签:

  1. const element = <div />;

然而,元素也可以表示用户自定义组件:

  1. const element = <Welcome name="Sara" />;

当React 看到一个元素代表一个用户自定义组件时,他将JSX 特性作为一个单独对象传递给组件,我们将这个对象为“props”。

例如,下面代码在页面上渲染“Hello, Sara”:

  1. function Welcome(props) {
  2. return <h1>Hello, {props.name}</h1>
  3. }
  4. const element = <Welcome name="Sara" />;
  5. ReactDOM.render(
  6. element,
  7. document.getElementById('root')
  8. );

在CodePen 上尝试

让我们简要介绍一下这个例子中发生了什么:

  1. 我们调用ReactDOM.render() 使用<Welcome name="Sara"> 元素。
  2. React 调用Welcome 组件将{name: 'Sara'} 作为props。
  3. 我们的Welcome 组件返回一个<h1>Hello, Sara</h1> 元素作为结果。
  4. React DOM 有效地更新DOM 去匹配<h1>Hello, Sara</h1>

附加说明:

总是使用大写字母来开始命名一个组件。

例如,<div /> 代表一个DOM 标签,但那是<Welcome /> 代表一个组件,并且需要Welcome 在作用域范围内。

Composing Components

组件可以在其输出中引用其他组件。这让我们可以对任何级别的细节使用相同的组件抽象。按钮、表单、对话框、一屏(screen):在React 应用中,所有的这些都可以表示为组件。

例如,我们创建一个App 组件来渲染多次Welcome 组件。

  1. function Welcome(props) {
  2. return <h1>Hello, {props.name}</h1>;
  3. }
  4. function App(){
  5. return (
  6. <div>
  7. <Welcome name="Sara" />
  8. <Welcome name="Cahal" />
  9. <Welcome name="Edite" />
  10. </div>
  11. );
  12. }
  13. ReactDOM.render(
  14. <App />,
  15. document.getElementById('root')
  16. );

在CodePen 上尝试

通常来说,一个新React 应用在顶部只有一个App 组件。 但是,如果你将React 集成到一个现有的应用中,你可能从一个小的组件例如Button 来自下而上开始,然后逐渐到视图层的顶级。

Extracting Components

不必担心将组件拆分成更小的组件。

例如,考虑这个Comment 组件:

  1. function Comment(props) {
  2. return (
  3. <div className="Comment">
  4. <div className="UserInfo">
  5. <img className="Avatar"
  6. src={props.author.avatarUrl}
  7. alt={props.author.name}
  8. />
  9. <div className="UserInfo-name">
  10. {props.author.name}
  11. </div>
  12. </div>
  13. <div className="Comment-text">
  14. {props.text}
  15. </div>
  16. <div className="Comment-date">
  17. {formatDate(props.date)}
  18. </div>
  19. </div>
  20. }
  21. );

在CodePen 上尝试

它接受author(一个对象),text(一个字符串)、和date(一个日期)作为props,来描述一个社交媒体网络评论框。

这个组件去改变很难因为所有的嵌套,也很那去复用它的各个部分。让我们从它提取几个组件。

首先,我们提取一个Avatar

  1. function Avatar(props) {
  2. return (
  3. <img className="Avatar"
  4. src={props.user.avatarUrl}
  5. alt={props.user.name}
  6. />
  7. )
  8. }

这个Avatar 并不需要知道它在Comment 中被渲染。这是就是为什么我们给他的props 一个更通用的名字:user 而不是author

我们建议从组件的自己的视角命名props,而不是从它被使用的上下文环境。

现在我们可以简化一点点Comment

  1. function Comment(props) {
  2. return (
  3. <div className="Comment">
  4. <div className="UserInfo">
  5. <Avatar user={props.author} />
  6. <div className="UserInfo-name">
  7. {props.author.name}
  8. </div>
  9. </div>
  10. <div className="Comment-text">
  11. {props.text}
  12. </div>
  13. <div className="Comment-date">
  14. {formatDate(props.date)}
  15. </div>
  16. </div>
  17. )
  18. };

接下来,我们将Avatar 和临近它的用户名字提取为UserInfo 组件:

  1. function UserInfo(props) {
  2. return (
  3. <div className="UserInfo">
  4. <Avatar user={props.author} />
  5. <div className="UserInfo-name">
  6. {props.user.name}
  7. </div>
  8. </div>
  9. );
  10. }

这能让我们更进一步简化Comment

  1. function Comment(props) {
  2. return (
  3. <div className="Comment">
  4. <UserInfo user={props.author} />
  5. <div className="Comment-text">
  6. {props.text}
  7. </div>
  8. <div className="Comment-date">
  9. {formatDate(props.date)}
  10. </div>
  11. </div>
  12. );
  13. }

在CodePen 上尝试

一开始提取组件似乎是繁重的工作,但是在一个比较大的应用中会获取很多可复用的组件。一个好的经验法则是,如果你的UI 的一部分被多次使用(ButtonPanelAvatar),或是对自己足够复杂(AppFeedStoryComment),对一个可复用的组件来说是一个很好的选择。

Props are Read-Only

不管你使用as a function or a class 声明一个组件,它决不允许修改它自己的props。考虑下来这个sum 函数:

  1. function sum(a , b) {
  2. return a + b;
  3. }

这种函数被称为pure 因为它们不被允许修改它们的输入,并且相同输入总是返回相同的结果。

相反,如果它能够修改自己的输入被称为impure:

  1. function withdraw(account, amount) {
  2. account.total -= amount;
  3. }

React 是相当的灵活的但是它有一个单一的严格的规则: All React Compoents must act like pure functions with respect to their props.

当然,应用UI 是动态的可以随着时间改变的。在下一章中,我们将介绍一个新的概念state。State 允许组件随着事件改变它们的输出响应用户的行为(user action),网络响应(network responses),其他任何事情,而不打破这个规则。