此文章是翻译Components and Props这篇React(版本v16.2.0 )官方文档。
Components and Props
组件让你将组件分拆独立的、可复用的块,并考虑独立的每一个部分。
从概念上讲,组件类似于JavaScript 函数。它们接受任意的输入(被称为“props”)并且返回React 元素描述在屏幕上如何展示。
Functional and Class Components
最简单的定义一个组件的方式是写一个JavaScript 函数:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
这个函数是一个有效的React 组件因为它接受一个“props”(代表属性(properties))对象参数接受数据并且返回一个React 元素。我们称这样的组件为“函数式(funcitional)”因为它们是字面量JavaScript 函数。
你也可以使用ES6 class 去定义一个组件:
class Welcome extends React.Component {
render(){
return <h1>Hello, {this.props.name}</h1>;
}
}
从React 的View 角度来看上述两个组件是相等的。
类有一些额外的特性,这些特性将在下一章 讨论。在此之前,我们将使用函数式组件来简化。
Rendering a Component
之前,我们只遇到使用React 元素来代表DOM 标签:
const element = <div />;
然而,元素也可以表示用户自定义组件:
const element = <Welcome name="Sara" />;
当React 看到一个元素代表一个用户自定义组件时,他将JSX 特性作为一个单独对象传递给组件,我们将这个对象为“props”。
例如,下面代码在页面上渲染“Hello, Sara”:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
让我们简要介绍一下这个例子中发生了什么:
- 我们调用
ReactDOM.render()
使用<Welcome name="Sara">
元素。 - React 调用
Welcome
组件将{name: 'Sara'}
作为props。 - 我们的
Welcome
组件返回一个<h1>Hello, Sara</h1>
元素作为结果。 - React DOM 有效地更新DOM 去匹配
<h1>Hello, Sara</h1>
。
附加说明:
总是使用大写字母来开始命名一个组件。
例如,
<div />
代表一个DOM 标签,但那是<Welcome />
代表一个组件,并且需要Welcome
在作用域范围内。
Composing Components
组件可以在其输出中引用其他组件。这让我们可以对任何级别的细节使用相同的组件抽象。按钮、表单、对话框、一屏(screen):在React 应用中,所有的这些都可以表示为组件。
例如,我们创建一个App
组件来渲染多次Welcome
组件。
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App(){
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
通常来说,一个新React 应用在顶部只有一个App
组件。 但是,如果你将React 集成到一个现有的应用中,你可能从一个小的组件例如Button
来自下而上开始,然后逐渐到视图层的顶级。
Extracting Components
不必担心将组件拆分成更小的组件。
例如,考虑这个Comment
组件:
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<img className="Avatar"
src={props.author.avatarUrl}
alt={props.author.name}
/>
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
}
);
它接受author
(一个对象),text
(一个字符串)、和date
(一个日期)作为props,来描述一个社交媒体网络评论框。
这个组件去改变很难因为所有的嵌套,也很那去复用它的各个部分。让我们从它提取几个组件。
首先,我们提取一个Avatar
:
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
)
}
这个Avatar
并不需要知道它在Comment
中被渲染。这是就是为什么我们给他的props 一个更通用的名字:user
而不是author
。
我们建议从组件的自己的视角命名props,而不是从它被使用的上下文环境。
现在我们可以简化一点点Comment
:
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<Avatar user={props.author} />
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
)
};
接下来,我们将Avatar
和临近它的用户名字提取为UserInfo
组件:
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.author} />
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
);
}
这能让我们更进一步简化Comment
:
function Comment(props) {
return (
<div className="Comment">
<UserInfo user={props.author} />
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
一开始提取组件似乎是繁重的工作,但是在一个比较大的应用中会获取很多可复用的组件。一个好的经验法则是,如果你的UI 的一部分被多次使用(Button
、Panel
、Avatar
),或是对自己足够复杂(App
、FeedStory
、Comment
),对一个可复用的组件来说是一个很好的选择。
Props are Read-Only
不管你使用as a function or a class 声明一个组件,它决不允许修改它自己的props。考虑下来这个sum
函数:
function sum(a , b) {
return a + b;
}
这种函数被称为pure 因为它们不被允许修改它们的输入,并且相同输入总是返回相同的结果。
相反,如果它能够修改自己的输入被称为impure:
function withdraw(account, amount) {
account.total -= amount;
}
React 是相当的灵活的但是它有一个单一的严格的规则: All React Compoents must act like pure functions with respect to their props.
当然,应用UI 是动态的可以随着时间改变的。在下一章中,我们将介绍一个新的概念state。State 允许组件随着事件改变它们的输出响应用户的行为(user action),网络响应(network responses),其他任何事情,而不打破这个规则。