美文网首页
React-redux深入理解

React-redux深入理解

作者: 一个笑点低的妹纸 | 来源:发表于2017-07-21 20:04 被阅读476次

    首先,一张 Redux 解释图镇楼:

    Redux explained(图片来源:https://github.com/buckyroberts/React-Redux-Boilerplate)
    【回顾】Redux 的核心: store 是什么?(createStore 函数的实现)
    const store = createStore(reducer);
    

    store 是一个对象,包含3个方法:getStatedispatchsubscribe

    // createStore 函数实现
    const createStore = (reducer) => {
        let state;
        let listeners = [];
    
        const getState = () => state;
        const dispatch = (action) => {
            state = reducer(state, action);
            listeners.forEach(listener => listener());
        }
        const subscribe = (listener) => {    // listener 就是一个要执行的函数
            listeners.push(listener);
            return () => {  // 采用柯里化方式注销监听器,用法:store.subscribe(listener)();
                listeners = listeners.filter(l => l != listener);
            }
        }
    
        dispatch({});   // 初始化state
    
        return { getState, dispatch, subscribe }
    }
    

    由函数可知,当用户 dispatch 一个 action 时,会自动调用 reducer 从而得到最新的 state,该 state 可通过 getState 函数获取,并且会执行所有已注册的函数。

    所以,redux 的套路就是(参考 React小书 ):

    // 定一个 reducer
    function reducer (state, action) {
      /* 初始化 state 和 switch case */
    }
    
    // 生成 store
    const store = createStore(reducer)
    
    // 监听数据变化重新渲染页面,即更新状态的过程
    store.subscribe(() => renderApp(store.getState()))
    
    // 首次渲染页面
    renderApp(store.getState()) 
    
    // 后面可以随意 dispatch 了,页面自动更新
    store.dispatch(...)
    
    【问题】:React 和 Redux 之间是如何连接?

    从图中可以看到,Store 通过 Provider 传递给了我们的 React 组件,因此,使得组件能够获取到 store。那么它是如何将做到的呢?

    为了弄明白 React 和 Redux 之间是如何连接的,我们需要了解以下一些内容(参考 React小书 ):

    一、背景:React 中父组件 context 的作用,用以摆脱状态提升

    在 React 中,父组件使用 getChildContext(),可以将 store 放到它的 context 里面,相当于给子组件设置了一个全局变量,这样每个子组件就都可以获取到 store。

    // 父组件
    class Index extends Component {
      // 提供 context 的组件必须提供 childContextTypes 作为 context 的声明和验证
      static childContextTypes = {
        store: PropTypes.object
      }
    
      // 一个组件可以通过 getChildContext 方法返回一个对象,这个对象就是子树的 context
      getChildContext () {
        return { store }
      }
    
      render () {
        return (
          <div>
            <Header />
            <Content />
          </div>
        )
      }
    }
    
    // 子组件
    class Header extends Component {
      // 声明想要的 context 里面的哪些状态,以便通过 this.context 进行访问
      // 子组件要获取 context 里面的内容的话,就必须写 contextTypes 来声明和验证你需要获取的状态的类型
      static contextTypes = {
        store: PropTypes.object
      }
    
      constructor () {
        super()
        this.state = { themeColor: '' }
      }
    
      componentWillMount () {
        this._updateThemeColor()
      }
    
      _updateThemeColor () {
        // 子组件可以访问到父组件 context 里面的内容
        const { store } = this.context
        const state = store.getState()
        this.setState({ themeColor: state.themeColor })
      }
    
      render () {
        return (
          <h1 style={{ color: this.state.themeColor }}>React.js 小书</h1>
        )
      }
    }
    

    如果一个组件设置了 context,那么它的子组件都可以直接访问到里面的内容,它就像这个组件为根的子树的全局变量。任意深度的子组件都可以通过 contextTypes 来声明你想要的 context 里面的哪些状态,然后可以通过 this.context 访问到那些状态。

    context 存在的问题:首先,它是一个试验性的API,不稳定,可能会改变,虽然好多库都用到了这个特性;其次它是脆弱的,如果在层级中的任何一个组件执行了 shouldComponentUpdate 返回 false,context 则不会传递给其之后所有的子组件。

    二、react-redux 的诞生

    因为 context 是一个比较危险的特性,我们不想在自己写组件的时候被其污染,我们需要将其剥离出来,因此,react-redux 诞生了,其中的 Provider 以及 connect 就帮助我们将 React 的组件和 Redux 的 store 进行了连接。

    1. Provider 的实现

    作用:充当父组件的作用,把 store 放到自己的 context 里面,让子组件 connect 的时候获取。

    export class Provider extends Component {
      static propTypes = {
        store: PropTypes.object,
        children: PropTypes.any
      }
    
      static childContextTypes = {
        store: PropTypes.object
      }
    
      getChildContext () {
        return {
          store: this.props.store
        }
      }
    
      render () {
        return (
          <div>{this.props.children}</div>
        )
      }
    }
    
    2. 高阶组件 connect(connect 实现)

    高阶组件:高阶组件是一个接受一个组件为参数,并返回一个被包装过的组件的函数,即返回传入props的原组件。
    connect 的作用:和 React 的 context 打交道,将 context 中的数据取出来,并以 prop 的形式传递给 Dumb 组件。

    const mapStateToProps = (state) => { themeColor: state.themeColor }
    const mapDispatchToProps = (dispatch) => ({
        onSwitchColor(color) => {
          dispatch({ type: 'CHANGE_COLOR', themeColor: color })
        }
    });
    
    // connect 实现
    // connect 接受 mapStateToProps 和 mapDispatchProps 参数后,返回的函数是高阶组件,该高阶组件接受一个组件作为参数,然后用 Connect 包装之后返回
    export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {
      class Connect extends Component {
        static contextTypes = {
          store: PropTypes.object
        }
    
        constructor () {
          super()
          this.state = {
            allProps: {}
          }
        }
    
        componentWillMount () {
          const { store } = this.context
          this._updateProps()
          store.subscribe(() => this._updateProps())
        }
    
        _updateProps () {
          const { store } = this.context
          let stateProps = mapStateToProps
            ? mapStateToProps(store.getState(), this.props)
            : {} // 防止 mapStateToProps 没有传入
          let dispatchProps = mapDispatchToProps
            ? mapDispatchToProps(store.dispatch, this.props)
            : {} // 防止 mapDispatchToProps 没有传入
          this.setState({
            allProps: {
              ...stateProps,
              ...dispatchProps,
              ...this.props
            }
          })
        }
    
        render () {
          return <WrappedComponent {...this.state.allProps} />
        }
      }
      return Connect
    }
    

    connect 接口:

    connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])(Component)
    

    三、react-redux 的性能优化

    react-redux性能优化之reselect

    三、文章参考

    React小书
    Redux使用小结

    相关文章

      网友评论

          本文标题:React-redux深入理解

          本文链接:https://www.haomeiwen.com/subject/nzqfkxtx.html