复合组件
最后更新于:2022-04-01 20:47:44
目前为止,我们已经学了如何用单个组件来展示数据和处理用户输入。下一步让我们来体验 React 最激动人心的特性之一:可组合性(composability)。
[TOC]
## 动机:关注分离
通过复用那些接口定义良好的组件来开发新的模块化组件,我们得到了与使用函数和类相似的好处。具体来说就是能够通过开发简单的组件把程序的_不同关注面分离_。如果为程序开发一套自定义的组件库,那么就能以最适合业务场景的方式来展示你的用户界面。
## 组合实例
一起来使用 Facebook Graph API 开发显示个人图片和用户名的简单 Avatar 组件吧。
~~~
var Avatar = React.createClass({
render: function() {
return (
);
}
});
var ProfilePic = React.createClass({
render: function() {
return (
);
}
});
var ProfileLink = React.createClass({
render: function() {
return (
{this.props.username}
);
}
});
React.render(
,
document.getElementById('example')
);
~~~
## 从属关系
上面例子中,`Avatar` 拥有 `ProfilePic` 和 `ProfileLink` 的实例。`拥有者` 就是给其它组件设置 `props` 的那个组件。更正式地说, 如果组件 `Y` 在 `render()` 方法是创建了组件 `X`,那么 `Y` 就拥有 `X`。上面讲过,组件不能修改自身的 `props` - 它们总是与它们拥有者设置的保持一致。这是保持用户界面一致性的关键性原则。
把从属关系与父子关系加以区别至关重要。从属关系是 React 特有的,而父子关系简单来讲就是DOM 里的标签的关系。在上一个例子中,`Avatar` 拥有 `div`、`ProfilePic` 和`ProfileLink` 实例,`div` 是 `ProfilePic` 和 `ProfileLink` 实例的**父级**(但不是拥有者)。
## 子级
实例化 React 组件时,你可以在开始标签和结束标签之间引用在React 组件或者Javascript 表达式:
~~~
~~~
`Parent` 能通过专门的 `this.props.children` props 读取子级。**`this.props.children` 是一个不透明的数据结构:** 通过 [React.Children 工具类](http://reactjs.cn/react/docs/top-level-api.html#react.children) 来操作。
### 子级校正(Reconciliation)
**校正就是每次 render 方法调用后 React 更新 DOM 的过程。** 一般情况下,子级会根据它们被渲染的顺序来做校正。例如,下面代码描述了两次渲染的过程:
~~~
// 第一次渲染
// 第二次渲染
~~~
直观来看,只是删除了`Paragraph 1`。事实上,React 先更新第一个子级的内容,然后删除最后一个组件。React 是根据子级的_顺序_来校正的。
### 子组件状态管理
对于大多数组件,这没什么大碍。但是,对于使用 `this.state` 来在多次渲染过程中里维持数据的状态化组件,这样做潜在很多问题。
多数情况下,可以通过隐藏组件而不是删除它们来绕过这些问题。
~~~
// 第一次渲染
// 第二次渲染
~~~
### 动态子级
如果子组件位置会改变(如在搜索结果中)或者有新组件添加到列表开头(如在流中)情况会变得更加复杂。如果子级要在多个渲染阶段保持自己的特征和状态,在这种情况下,你可以通过给子级设置惟一标识的 `key` 来区分。
~~~
render: function() {
var results = this.props.results;
return (
{this.props.data.text} ;
}
});
var MyComponent = React.createClass({
render: function() {
return (
{this.props.data.text} ;
}
});
var MyComponent = React.createClass({
render: function() {
return (
{result.text} ;
});
return (
';
Paragraph 1
Paragraph 2
Paragraph 2
Paragraph 1
Paragraph 2
Paragraph 1
Paragraph 2
-
{results.map(function(result) {
return
- {result.text} ; })}
-
{this.props.results.map(function(result) {
return
-
{this.props.results.map(function(result) {
return
-
{items}