引言
组件将UI划分成独立的,可复用的片段,并对每个组件进行独立的构思。本节将会介绍组件的构建思路。
概念上来说,组件就好像是JavaScript里的函数。它们能够接受任意的入参(称作props)并返回描述屏幕展示UI的React元素。
函数组件&类组件
定义组件最简单的方法就是写一个JavaScript函数。
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
不用担心,这是一个可使用的组件,因为它接收了一个带有数据的props(也可以用properties代替)作为参数对象并且返回了一个React元素。我们把这种组件成为函数组件(因为这就是一个函数)。
您也可以使用ES6 class来定义组件。
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
上述两个组件从React的组件定义来看是等价的。
类组件还有一些特性我们将会放到下一节讲解。在那之前我们将使用函数组件来进行示例讲解。
渲染组件
之前我们讲的都只是用React元素来代表DOM标签。
const element = <div />;
然而,React元素也可以表示用户定义的组件。
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>。
提示:定义组件时组件名要以大写字母开头 React将以小写字母开头的组件视为DOM标签,例如
<div/>表示一个HTML的 div 标签。但是<Welcome />表示组件并且要在作用域范围内使用它。
组合组件
组件能在它的返回元素中使用其他组件。这使得我们可以在任意层级上使用组件。按钮、表单、对话框、界面,在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')
);
当您使用create-react-app创建一个React应用时,您会发现在应用中App组件作为顶层组件被使用。但是如果您是在现有应用中整合React,那么您可以自底向上地从使用一个小组件开始,逐步地将React组件应用到您应用的各个层级上。
提取组件
不要害怕将组件分解成更小的组件。
举个例子,让我们观察下面的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(object),text(string),date(date)作为props,并且描述了社交网站上的一个评论。
由于这个组件嵌套了太多层的内容,并且其中的内容很难复用。所以,让我们来从中提取组件吧。
首先,我们来提取Avatar组件。
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
Avatar组件不需要知道它是否被渲染在Comment组件里,因此在这里我们将它的prop起了一个更普适的名字user。
我们推荐在给prop起名时从组件的角度上考虑,而不是从使用组件的上下文的联想上考虑。
现在我们在编写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>
);
}
接下来,我们将提取UserInfo组件来渲染带有名字的头像。
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<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),或者这个页面/组件本身很复杂时,那么您就可以把他提取成组件了。
Props是只读的
不管您是声明了一个函数组件还是类组件,它的props是不能更改的。我们来观察下面的代码。
function sum(a, b) {
return a + b;
}
这样的函数称为纯函数,因为它们不会改变它们的入参,并且在相同的入参下永远返回相同的结果。
相反,下面的函数就不是纯函数,因为它改变了它的入参。
function withdraw(account, amount) {
account.total -= amount;
}
React非常灵活,但是却有一个严格的规则:
所有的组件都必须想纯函数一样保护它们的props不被更改
不过,UI是动态的并且总是在改变,在下一节,我们将会介绍一个新概念“状态(state)”。状态可以让组件在不违反上述规则的情况下更具用户的动作或者网络传输结果改变输出。