3.2.3 容器组件和傻瓜组件
您可以在百度里搜索“深入浅出React和Redux 艾草文学(www.321553.xyz)”查找最新章节!
3.2.3 容器组件和傻瓜组件
分析一下上面的Redux例子中的Counter组件和Summary组件部分,可以发现一个规律,在Redux框架下,一个React组件基本上就是要完成以下两个功能:
·和Redux Store打交道,读取Store的状态,用于初始化组件的状态,同时还要监听Store的状态改变;当Store状态发生变化时,需要更新组件状态,从而驱动组件重新渲染;当需要更新Store状态时,就要派发action对象;
·根据当前props和state,渲染出用户界面。
还记得那句话吗?让一个组件只专注做一件事,如果发现一个组件做的事情太多了,就可以把这个组件拆分成多个组件,让每个组件依然只专注做一件事。
如果React组件都是要包办上面说的两个任务,似乎做的事情也的确稍微多了一点。我们可以考虑拆分,拆分为两个组件,分别承担一个任务,然后把两个组件嵌套起来,完成原本一个组件完成的所有任务。
这样的关系里,两个组件是父子组件的关系。业界对于这样的拆分有多种叫法,承担第一个任务的组件,也就是负责和Redux Store打交道的组件,处于外层,所以被称为容器组件(Container Component);对于承担第二个任务的组件,也就是只专心负责渲染界面的组件,处于内层,叫做展示组件(Presentational Component)。
外层的容器组件又叫聪明组件(Smart Com-ponent),内层的展示组件又叫傻瓜组件(Dumb Component),所谓“聪明”还是“傻瓜”只是相对而言,并没有褒贬的含义。如图3-4所示。
图3-4 容器组件和傻瓜组件的分工
傻瓜组件就是一个纯函数,根据props产生结果。说是“傻瓜”,我倒是觉得这种纯函数实现反而体现了计算机编程中的大智慧,大智若愚。
而容器组件,只是做的事情涉及一些状态转换,虽然名字里有“聪明”,其实做的事情都有套路,我们很容易就能抽取出共同之处,复用代码完成任务,并不需要开发者极其聪明才能掌握。
在我们把一个组件拆分为容器组件和傻瓜组件的时候,不只是功能分离,还有一个比较大的变化,那就是傻瓜组件不需要有状态了。
实际上,让傻瓜组件无状态,是我们拆分的主要目的之一,傻瓜组件只需要根据props来渲染结果,不需要state。
那么,状态哪里去了呢?全都交给容器组件去打点,这是它的责任。容器组件如何把状态传递给傻瓜组件呢?通过props。
值得一提的是,拆分容器组件和傻瓜组件,是设计React组件的一种模式,和Redux没有直接关系。在Flux或者任何一种其他框架下都可以使用这种模式,只不过为了引出后面的react-redux,我们才在这里开始介绍罢了。
我们还是通过例子来感受一下容器组件和傻瓜组件如何协同工作,对应的代码在chapter-03/redux_smart_dumb目录下,是前面chapter-03/redux_basic的改进,只有视图部分代码有改变。
在视图代码src/views/Counter.js中定义了两个组件,一个是Counter,这是傻瓜组件,另一个是CounterContainer,这是容器组件。
傻瓜组件Counter代码的逻辑前所未有的简单,只有一个render函数,代码如下:
class Counter extends Component {
render() {
const {caption, onIncrement, onDecrement, value} = this.props;
return (
+
-
{caption} count: {value}
);
}
}
可以看到,Counter组件完全没有state,只有一个render方法,所有的数据都来自于props,这种组件叫做“无状态”组件。
而CounterContainer组件承担了所有的和Store关联的工作,它的render函数所做的就是渲染傻瓜组件Counter而已,只负责传递必要的prop,相关代码如下:
class CounterContainer extends Component {
render() {
return onIncrement={this.onIncrement} onDecrement={this.onDecrement} value={this.state.value} /> } } export default CounterContainer; 可以看到,这个文件export导出的不再是Counter组件,而是CounterContainer组件,也就是对于使用这个视图的模块来说,根本不会感受到傻瓜组件的存在,从外部看到的就只是容器组件。 对于无状态组件,其实我们可以进一步缩减代码,React支持只用一个函数代表的无状态组件,所以,Counter组件可以进一步简化,代码如下: function Counter (props) { const {caption, onIncrement, onDecrement, value} = props; return ( + - {caption} count: {value} ); } 因为没有状态,不需要用对象表示,所以连类都不需要了,对于一个只有render方法的组件,缩略为一个函数足矣。 注意,改为这种写法,获取props就不能用this.props,而是通过函数的参数props获得,无状态组件的props参数和有状态组件的this.props内容和结构完全一样。 还有一种惯常写法,就是把解构赋值(destructuring assignment)直接放在参数部分。 function Counter ({caption, onIncrement, onDecrement, value} { // 函数体中可以直接使用caption、onIncrement等变量 } 看src/views/Summary.js中,内容也被分解为了傻瓜组件Summary和SummaryCon-tainer,方式和Counter差不多,不再赘述。 重新审阅代码,我们可以看到CounterSummary和SummaryContainer代码有很多相同之处,写两份实在是重复,既然都是套路,完全可以抽取出来,后面的章节会讲如何应用react-redux来减少重复代码。 深入浅出React和Redux