react-redux

react-redux

react-redux是什么

1) 一个react插件库

2) 专门用来简化react应用中使用redux

上一篇中我们已经利用redux实现了基本的通信需求. 然而为了简化其写法.

读取数据 采用let state = this.props.store.getState();每一次读取都需要调用getState

修改数据 采用this.props.store.dispatch({type:INCREASE,data: num }) 每一次修改都需要dispatch.

我们可以通过react-redux插件来用更简单的写法更加优雅地实现上述功能.(以及解决多层传递问题)

思考一下react-redux到底干了什么?

首先让我们回忆一下redux在干什么?

redux本质是实现 集中式管理react应用中多个组件共享的状态 , 是组件间通信的第三种方式. 通过redux中的store来存储状态与方法, 而不是将状态与方法存放在组件中.

redux的思路: component只提供接口(props各属性), 通过各属性接受状态与方法.

redux实现步骤:

  1. redux部分:

    1. 定义状态, 状态变化规则 (各类reducer函数 ), 根据state和action(type, data)返回新的state.

      目前暂且state的定义还是在传参的默认值设定.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      export function counter(state =0, action){
      switch (action.type){
      case INCREASE:
      return state + action.data
      case DECREASE:
      return state - action.data
      case INCREASASYCN:
      return state + action.data
      default:
      return state;
      }
      }
    2. 根据reducer生成store,const store = createStore(counter);

    3. store传入所需component, <App store={store} />

    4. store与render挂钩, 手动重绘 store.subscribe(render)

  2. component部分

    1. 在props的store中获取状态, this.props.store.getState();
    2. 在修改数据的方法其实并没有真实地发送给component, 而提供store的dispatch方法, 传入一个action, 进而在store中自己利用reducer来进行状态的改变. ( 而这也是为什么要手动重绘的核心 )
  3. 其他辅助部分

    1. action-types 定义action type, 将字符串封装成变量
     2. action-creator 提供action生成函数

对比一下react-redux是如何解决上述问题的

​ React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)。

UI 组件有以下几个特征。

  • 只负责 UI 的呈现,不带有任何业务逻辑
  • 没有状态(即不使用this.state这个变量)
  • 所有数据都由参数(this.props)提供
  • 不使用任何 Redux 的 API

容器组件的特征恰恰相反。

  • 负责管理数据和业务逻辑,不负责 UI 的呈现
  • 带有内部状态
  • 使用 Redux 的 API

其实和redux的解决思路的区别在于: redux是将整个store全部交给 presentational component, 因此其中会使用redux的API. 而react-redux则将store的解析交由connect函数处理.

  1. redux部分:

    1. 定义状态, 状态变化规则 (各类reducer函数 ), 根据state和action(type, data)返回新的state.

      目前暂且state的定义还是在传参的默认值设定.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      export function counter(state =0, action){
      switch (action.type){
      case INCREASE:
      return state + action.data
      case DECREASE:
      return state - action.data
      case INCREASASYCN:
      return state + action.data
      default:
      return state;
      }
      }
    2. 根据reducer生成store,const store = createStore(counter);

  1. 不再是 store传入所需component, <App store={store} />

    变为 而是将store传给Provider组件

    1
    2
    3
    4
    import {Provider} from 'react-redux';
    <Provider store={store}>
    <App />
    </Provider>
  2. 不再是 store与render挂钩, 手动重绘 store.subscribe(render)

    变为 无需手动订阅, 在connect中自动订阅.

  1. component部分

    1. UIcomponent需要的store中的数据和方法都默认从props中获取.this.props.count this.props.increase(num)

    2. 使用connect将store中的数据抽离交给UIcomponent.

      1
      2
      3
      4
      export default connect(
      state =>({count: state}), //mapStateToProps,
      {increase: increaseCreator, decrease:dereaseCreator}//mapDispatchToProps
      )(Counter)// UI component

      mapStateToProps

      mapStateToProps是一个函数。建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系。应该返回一个对象,里面的每一个键值对就是一个映射。

      mapStateToProps订阅 Store,每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。

      mapStateToProps的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象, 用于获取组件的内部数据。

      connect方法可以省略mapStateToProps参数,那样的话,UI 组件就不会订阅Store,就是说 Store 的更新不会引起 UI 组件的更新。

      mapDispatchToProps

      mapDispatchToPropsconnect函数的第二个参数,用来建立 UI 组件的参数到store.dispatch方法的映射。

      如果mapDispatchToProps是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出。对应本例子, 在UI component中调用对应 Action creator的同名参数, 其实this.props.increase(num)等价于纯redux版本中的this.props.store.dispatch(actionCreator.increaseCreator(num))即: 生成action后自动发出.

  2. 其他辅助部分

    1. action-types 定义action type, 将字符串封装成变量
        2. action-creator 提供action生成函数

react-redux优势

  1. presentational component 中不再包含redux API, 很纯粹

  2. 多级传递被解决!state对象作为参数,传入容器组件。但是,这样做比较麻烦,尤其是容器组件可能在很深的层级,一级级将state传下去就很麻烦。Provider在根组件外面包了一层,这样一来,App的所有子组件就默认都可以拿到state了。

    原理: React组件的context属性,请看源码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Provider extends Component {
    getChildContext() {
    return {
    store: this.props.store
    };
    }
    render() {
    return this.props.children;
    }
    }

    Provider.childContextTypes = {
    store: React.PropTypes.object
    }

    上面代码中,store放在了上下文对象context上面。然后,子组件就可以从context拿到store,代码大致如下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class VisibleTodoList extends Component {
    componentDidMount() {
    const { store } = this.context;
    this.unsubscribe = store.subscribe(() =>
    this.forceUpdate()
    );
    }

    render() {
    const props = this.props;
    const { store } = this.context;
    const state = store.getState();
    // ...
    }
    }

    VisibleTodoList.contextTypes = {
    store: React.PropTypes.object
    }

    React-Redux自动生成的容器组件的代码,就类似上面这样,从而拿到store

补充

使用React-Router的项目,与其他项目没有不同之处,也是使用ProviderRouter外面包一层,毕竟Provider的唯一功能就是传入store对象。

1
2
3
4
5
6
7
const Root = ({ store }) => (
<Provider store={store}>
<Router>
<Route path="/" component={App} />
</Router>
</Provider>
);

参考文章:Redux 入门教程(三):React-Redux 的用法

评论