React 具有强大的组合模型,我们建议使用组合而不是继承来复用组件之间的代码。
撸码:
class CombineItem extends React.Component{
constructor(props){
super(props);
}
render(){
return (
<div>
<div className="left">
{this.props.left}
</div>
<div className="right">
{
this.props.right
}
</div>
</div>
);
};
}
class Combine extends React.Component{
constructor(props){
super(props);
}
render(){
return (
<div>
<CombineItem left={<LeftComponent />} right={<RightComponent/>}/>
</div>
);
}
}
function LeftComponent(){
const divStyle = {
backgroundColor: 'blue',
width: '150px',
height:'200px',
paddingTop: '10px',
display: 'inline-block'
};
return (
<div style={divStyle} />
); }
function RightComponent() {
let rightStyle = {backgroundColor:'red',width:'300px',height:'400px'};
return (
<div style={rightStyle} />
);
}
实践:左下图一个实例
第一步:把UI 划分出组件层级:
把此功能分成5层,分别为:
1.橙色:容器层
2.蓝色:用户输入层
3.绿色:数据集合层
4.绿松石色:每个分类的标题
5.红色:每个产品
即:
FilterableProductTable
SearchBar
-
ProductTable
ProductCategoryRow
ProductRow
第二步:用 React 创建一个静态版本
自顶向下 还是 自下而上 取决于你的项目是否足够庞大。
在这步的最后,你会拥有一个用于呈现数据模型的可重用组件库。这些组件只会有 render()
方法,因为这只是你的应用的静态版本。层级最高的组件(FilterableProductTable
)会把数据模型作为 prop 传入。如果你改变你的基础数据模型并且再次调用 ReactDOM.render()
, UI 会更新。很容易看到你的 UI 是如何更新的,哪里进行了更新。因为没有什么复杂的事情发生。React 的单向数据流(也叫作单向绑定)保证了一切是模块化并且是快速的。
第三步:定义 UI 状态的最小(但完整)表示
为了使你的 UI 交互,你需要能够触发对底层数据模型的更改。React 使用 state,让这变的更容易。
为了正确构建你的应用,首先你需要考虑你的应用所需要的最小可变状态集。要点是 DRY:不要重复(Don’t Repeat Yourself)。找出应用程序的绝对最小表示并计算你所需要的其他任何请求。例如,如果你正在创建一个 TODO 列表,只要保存一个包含 TODO 事项的数组;不要为计数保留一个单独的状态变量。相反,当你想要渲染 TODO 计数时,只需要使用 TODO 数组的长度就可以了。
想想我们的实例应用中所有数据。我们有:
- 原产品列表
- 用户输入的搜索文本
- 复选框的值
- 产品的筛选列表
让我们来看看每一条,找出哪一个是 state。每个数据只要考虑三个问题:
- 它是通过 props 从父级传来的吗?如果是,他可能不是 state。
- 它随着时间推移不变吗?如果是,它可能不是 state。
- 你能够根据组件中任何其他的 state 或 props 把它计算出来吗?如果是,它不是 state。
原产品列表被作为 props 传入,所以它不是 state。搜索文本和复选框似乎是 state,因为它们随时间改变并且不能由其他任何值计算出来。最后,产品的筛选列表不是 state,因为它可以通过将原始产品列表与搜索文本和复选框的值组合计算出来。
最后,我们的 state 有:
- 用户输入的搜索文本
- 复选框的值
第四部 :确定你的State 应该位于哪里
对你应用的每一个 state:
- 确定每一个需要这个 state 来渲染的组件。
- 找到一个公共所有者组件(一个在层级上高于所有其他需要这个 state 的组件的组件)
- 这个公共所有者组件或另一个层级更高的组件应该拥有这个 state。
- 如果你没有找到可以拥有这个 state 的组件,创建一个仅用来保存状态的组件并把它加入比这个公共所有者组件层级更高的地方。
让我们用这个策略分析我们的应用:
ProductTable
需要根据 state 过滤产品列表,SearchBar
需要展示搜索文本和复选框状态。- 公共所有者组件是
FilterableProductTable
。 - 筛选文本和复选框的值应该放在
FilterableProductTable
。
很酷,所以我们决定把 state 放在 FilterableProductTable
。首先,为 FilterableProductTable
的 constructor
添加一个实例属性 this.state = {filterText: '', inStockOnly: false}
来表示我们应用的初始状态。接下来,把 filterText
和 inStockOnly
作为 prop 传入 ProductTable
和 SearchBar
。最后在 ProductTable
中使用这些 props 来筛选每行产品信息,在 SearchBar
中设置表单域的值。
现在你能够看到你的应用是如何运作的了:设置 filterText
的值为 ball
并刷新你的应用。你会看到数据表格正确的更新了
第五步 : 添加反向数据流
到目前为止,我们已经创建了一个可以正确渲染的应用程序,它的数据在层级中通过函数的 props 和 state 向下流动。现在是时候支持其他方式的数据流了:层级结构中最底层的表单组件需要去更新在 FilterableProductTable
中的 state。
React 的数据流很明显,让你可以很轻松的了解你的程序是如何运行的,但相较于传统的双向绑定,它的代码量会稍微多一点。
在当前版本的示例中,如果你试图键入或选中复选框,你会发现 React 会忽略你的输入。这是故意的,因为我们把 input
的 value
属性设置为一直等于从 FilterableProductTable
传入的 state
.
让我们想想我们想要做什么。我们想确保每当用户更改表单时,我们更新状态来反应用户输入。因为组件应该只更新自己的状态, FilterableProductTable
会将一个回调函数传递给 SearchBar
,每当应该更新状态时,它就会触发。我们可以使用输入上的 onChange
事件来调用它。FilterableProductTable
传入的回调函数会调用 setState()
,这时应用程序会被更新。
虽然这听起来很复杂,但它只是几行代码的问题。而且,你可以清楚地看出你的应用中数据是如何流动的。