redux基础第二讲——react-redux

容器组件和展示组件

在上一讲的实例中可以发现,一个React组件基本上就完成两个功能:

  • 和store打交道,计算state
  • 渲染用户界面

拆分组件有一个原则,就是要让每个组件只承担一个功能,如果发现一个组件承担的事情太多,就要继续划分这个组件。

通常,我们把一个组件划分为容器组件和展示组件。容器组件(Container Component)主要负责处理数据,和store打交道,处于外层;展示组件(Presentationn Component)主要负责渲染界面,处于内层。

在我们把组件拆分为容器组件和展示组件的时候,不仅是功能的分类,还有一个比较大的变化,那就是展示组件不需要有状态。展示组件只需根据props来渲染结果,state的处理全部交给容器组件。

还是通过例子来感受一下容易组件和展示组件到底是如何协同的工作的。我们改变上一讲例子中的Counter组件。

class Counter extends Component {
  render() {
    const {caption, onIncrement, onDecrement, value} = this.props;

    return (
      <div>
        <button style={buttonStyle} onClick={onIncrement}>+</button>
        <button style={buttonStyle} onClick={onDecrement}>-</button>
        <span>{caption} count: {value}</span>
      </div>
    );
  }
}

可以看到,Counter组件完全没有state,只有一个render()函数,所有数据均来自props。这种组件也成为“无状态”组件。还可像下面这样简写:

function Counter({caption, onIncrement, onDecrement, value}) {
    return (
        <div>
            <button style={buttonStyle} onClick={onIncrement}>+</button>
            <button style={buttonStyle} onClick={onDecrement}>-</button>
            <span>{caption} count: {value}</span>
        </div>
    ); 
}

我们将所有关于state的计算全部交给CounterContainer组件中,它的render函数所要做的就是渲染Counter组件,负责传递必要的props。这些props的值均来自它的父组件ControlPanel和store中的state。

class CounterContainer extends Component {
  constructor(props) {
    ...
  }

  onIncrement() {
    store.dispatch(Actions.increment(this.props.caption));
  }

  onDecrement() {
    store.dispatch(Actions.decrement(this.props.caption));
  }
   
  ...

  render() {
    return <Counter caption={this.props.caption}
      onIncrement={this.onIncrement}
      onDecrement={this.onDecrement}
      value={this.state.value} />
  }
}

export default ControlPanel;

在Counter.js文件中,我们最终导出的是ControlPanel,而不再是Counter。也就是说对于使用这个组件的模块来说,根本不会感受到展示组件的存在,从外部看到的只有容器组件。

react-redux(代码在lesson3-redux/chapter3)

上面的例子可以看出,Counter组件要获取名为caption的prop,就得从CounterContainer组件传入,而CounterContaine组件名为caption的prop是从最外层的ControlPanel传入的。如果开发一个组件,一共要嵌套十层子组件,最里层的组件需要获取一个属性A,那我们岂不是要从最外层组件一直传递到第二层,第三层...第九层,第十层,一共要传递十次,而且第二层到第九层根本就不需要使用这个属性A!这种操作是多么的傻X!好在react-redux为我们解决了这个问题,不然我也不会写react系列的博客了。

react-redux提供了Provider来传递store中的state,需要接收store的子组件只需包裹connect函数返回的函数即可。下面,我们来详细讲解connect和provider.

connect

以Counter组件为例,先看代码:

// src/lesson3-redux/chapter3/views/Counter.js

function Counter({caption, onIncrement, onDecrement, value}) {
    return (
        <div>
            <button style={buttonStyle} onClick={onIncrement}>+</button>
            <button style={buttonStyle} onClick={onDecrement}>-</button>
            <span>{caption} count: {value}</span>
        </div>
    );
}

...

export default connect(mapStateToProps, mapDispatchToProps)(Counter);

connect是react-redux提供的一个方法,这个方法接收mapStateToProps和mapDispatchToProps两个参数,执行结果依然是一个函数,这个函数立刻执行,传入参数Counter这个展示组件。当然,我们也可以将connect的结果赋值给变量CounterContainer,然后在暴露CounterContainer。

connect做的工作,其实就是以下两点:

  • 把store的状态转化为内层展示组件的props
  • 把展示组件中用户的行为转化为派发给store的action

mapStateToProps函数实现的就是connect工作的第一点,代码如下:

function mapStateToProps(state, ownProps) {
    return {
        value: state[ownProps.caption]
    }
}

返回对象的value属性对应的就是Counter组件接收的props上的value。

把展示组件中用户的行为转化为派发给store的action,就是把展示组件暴露出来的函数类型的prop关联上dispatch函数的调用。mapDispatchToProps函数代码如下:

function mapDispatchToProps(dispatch, ownProps) {
    return {
        onIncrement: () => {
            dispatch(Actions.increment(ownProps.caption));
        },
        onDecrement: () => {
            dispatch(Actions.decrement(ownProps.caption));
        }
    }
}

mapStateToProps和mapDispatchToProps都可以接收第二个参数ownProps,表示直接传递给外层容器组件的props。

Provider

Provider功能主要为以下两点:

  • 在原应用组件上包裹一层,使原来整个应用成为Provider的子组件
  • 接收Redux的store作为props,通过context对象传递给子孙组件上的connect

context对象涉及Provider的实现原理,这里我们暂且不做介绍,大家只要知道有这个东西存在即可。Provider是react-redux提供的方法,只需在index.js文件中引入,然后包裹在最外层的组件上即可。代码如下:

...

import {Provider} from 'react-redux';
import store from './lesson3-redux/chapter3/Store';

ReactDOM.render(
    <Provider store={store}>
        <ControlPanel />
    </Provider>,
    document.getElementById('root')
);
...

至此,redux的基本用法已经讲完。下一讲,我们将结合redux和react实现一个完整的todoList应用,更进一步的理解redux和react。

posted @ 2018-08-15 16:57  webLion200  阅读(193)  评论(0编辑  收藏  举报