React-redux深刻理解

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

Redux explained(图片来源:https://github.com/buckyroberts/React-Redux-Boilerplate)

【回顾】Redux 的核心: store 是什么?(createStore 函数的实现)
const store = createStore(reducer);

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

// 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 函数获取,而且会执行全部已注册的函数。java

因此,redux 的套路就是(参考 React小书 ):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。那么它是如何将作到的呢?git

为了弄明白 React 和 Redux 之间是如何链接的,咱们须要了解如下一些内容(参考 React小书 ):github

1、背景:React 中父组件 context 的做用,用以摆脱状态提高

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

// 父组件
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 则不会传递给其以后全部的子组件。app

2、react-redux 的诞生

由于 context 是一个比较危险的特性,咱们不想在本身写组件的时候被其污染,咱们须要将其剥离出来,所以,react-redux 诞生了,其中的 Provider 以及 connect 就帮助咱们将 React 的组件和 Redux 的 store 进行了链接。ide

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)

3、react-redux 的性能优化

react-redux性能优化之reselect

3、文章参考

React小书
Redux使用小结

相关文章
相关标签/搜索