react-redux源码分析及实现原型(上)

redux做为大型应用的状态管理工具,若是想配合react使用,须要借助react-redux。 redux主要完成两件事情:react

  • 负责应用的状态管理,保证单向数据流
  • 当应用状态发生变化,触发监听器。

那么,若是想要将react和redux搭配使用,就须要react组件能够根据redux中所存储的状态(store)更新view。 而且能够改变store。其实react-redux主要就是完成了这两件事情。 第一,经过将store传入root组件的context,使子节点能够获取到 state。 第二,经过store.subscribe 订阅store的变化,更新组件。 另外还有对于性能的优化,减小没必要要的渲染。git

熟悉使用方法

首先咱们熟悉一下react-redux的基本使用github

import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import { createStore } from 'redux';
import { Provider, connect } from 'react-redux';

const reducer = (state, action) => {
  if (action.type === 'add') {
    return {
      ...state,
      count: state.count + 1,
    }
  }
  return state;
};

const store = createStore(reducer, { count: 1 });

const mapStateToProps = (state) => {
  return ({
    count: state.count,
  });
};

const mapDispatchToProps = dispatch => ({
  add: () => dispatch({ type: 'add' }),
});

const mergeProps = (state, props) =>({
  countStr: `计数: ${state.count}`,
  ...props,
});

const options = {
  pure: true,
};

class App extends React.Component {
  render() {
    return (
      <div> <p>{this.props.countStr}</p> <button onClick={this.props.add}>点击+1</button> </div>
    )
  }
}

const AppContainer = connect(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps,
  options,
)(App);

ReactDOM.render(
  <Provider store={store}> <AppContainer /> </Provider>,
  document.getElementById('root'));
复制代码

从上面的例子中咱们能够看出,react-redux使用很是简单,仅仅使用了两个API,Providerconnectredux

  • Provider: 接收从redux而来的store,以供子组件使用。
  • connect: 高阶组件,当组件须要获取或者想要改变store的时候使用。能够接受四个参数:
    • mapStateToProps:取store数据,传递给组件。
    • mapDispatchToProps:改变store数据。
    • mergeProps:能够在其中对 mapStateToProps, mapDispatchToProps的结果进一步处理
    • options:一些配置项,例如 pure.当设置为true时,会避免没必要要的渲染

源码解读

Provider

class Provider extends Component {
  getChildContext() {
    return { [storeKey]: this[storeKey], [subscriptionKey]: null }
  }

  constructor(props, context) {
    super(props, context)
    this[storeKey] = props.store;
  }

  render() {
    return Children.only(this.props.children)
  }
}
复制代码

够简单吧,仅仅是把store放在了context下。subscriptionKey 能够暂时不用管。缓存

connect

首先咱们先回忆一下connect使用方法:性能优化

connect(mapStateToProps,mapDispatchToProps,mergeProps,options)(App);
复制代码

connect源码:框架

export function createConnect({ connectHOC = connectAdvanced, mapStateToPropsFactories = defaultMapStateToPropsFactories, mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories, mergePropsFactories = defaultMergePropsFactories, selectorFactory = defaultSelectorFactory } = {}) {
  return function connect( mapStateToProps, mapDispatchToProps, mergeProps, { pure = true, // ... } = {} ) {
    // 封装了传入的mapStateToProps等函数,这里能够先理解为 initMapStateToProps = () => mapStateToProps 这种形式
    const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, 'mapStateToProps')
    const initMapDispatchToProps = match(mapDispatchToProps, mapDispatchToPropsFactories, 'mapDispatchToProps')
    const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')

    // 选择器(selector)的做用就是计算mapStateToProps,mapDispatchToProps, ownProps(来自父组件的props)的结果,
    // 并将结果传给子组件props。这就是为何你在mapStateToProps等三个函数中写的结果,子组件能够经过this.props拿到。
    // 选择器工厂函数(selectorFactory)做用就是建立selector
    return connectHOC(selectorFactory, {
      // 若是没有传,那么将不会监听state变化
      shouldHandleStateChanges: Boolean(mapStateToProps),

      // 传给selectorFactory的参数
      initMapStateToProps,
      initMapDispatchToProps,
      initMergeProps,
      pure,
      // ...
    })
  }
}

export default createConnect()
复制代码

这段咱们能够知道,connect是对connectHOC(connectAdvanced)的封装,connectAdvanced使用了 defaultSelectorFactory,来建立selector。 react-redux 默认的 selectorFactory 中包含了不少性能优化的部分(咱们一下子会看到)。 其实react-redux 也提供了connectAdvanced API,为了便于你们理解我经过改变开头的例子,了解一下selectorFactory 是如何工做的。dom

// const AppContainer = connect(
// mapStateToProps,
// mapDispatchToProps,
// mergeProps,
// options,
// )(App);

// 在以前咱们使用了connect,如今咱们使用 connectAdvanced 来实现一下。
// 主要是是实现 selectorFactory:
function selectorFactory(dispatch) {
  let result = {}
  const action = mapDispatchToProps(dispatch);

  return (nextState, nextOwnProps) => {
    const state = mapStateToProps(nextState);
    const nextResult = mergeProps(state, action, nextOwnProps);
    if (!shallowEqual(result, nextResult)) result = nextResult
    return result;
  }
}
const AppContainer = connectAdvanced(selectorFactory)(App);
复制代码

这是一个简单的 selectorFactory,主要体现了其是如何工做的,让你们有一个大概的了解。 下面来看一下react-redux是如何实现 selectorFactory 的ide

selectorFactory

在解读代码以前,我想先说一下options.pure的做用。不知道你们还记得吗,在开头的例子有一个配置项pure。做用是减小运算优化性能。当设置为false时,react-redux将不会优化,store.subscirbe事件触发,组件就会渲染,即便是没有用到的state更新,也会这样,举个例子函数

// 你们都知道reducer的写法
return {
  ...state,
  count: state.count + 1,
}

// 必须返回一个新对象,那么若是我只是修改原来 state 好比
state.count += 1;
return state;

// 你会发现组件将不会渲染,其实store里的值是发生变化的。
// 这时若是你非想这么写, 而后又想从新渲染怎么办?
// 就是将pure设置为false,这样组件就会彻底实时更新。
复制代码

举了个不恰当的例子,你们千万不要这么干。。。

源码:

export default function finalPropsSelectorFactory(dispatch, { initMapStateToProps, initMapDispatchToProps, initMergeProps, ...options }) {
  const mapStateToProps = initMapStateToProps(dispatch, options)
  const mapDispatchToProps = initMapDispatchToProps(dispatch, options)
  const mergeProps = initMergeProps(dispatch, options)

  if (process.env.NODE_ENV !== 'production') {
    verifySubselectors(mapStateToProps, mapDispatchToProps, mergeProps, options.displayName)
  }

  const selectorFactory = options.pure
    ? pureFinalPropsSelectorFactory
    : impureFinalPropsSelectorFactory

  return selectorFactory(
    mapStateToProps,
    mapDispatchToProps,
    mergeProps,
    dispatch,
    options
  )
}
复制代码

这里很好理解,是对 selectorFactory 的封装,根据 options.pure 的值,选取不一样的 SelectorFactory;

  • options.pure 为 false 时,使用 impureFinalPropsSelectorFactory
  • options.pure 为 true 时,使用 pureFinalPropsSelectorFactory

先来看看简单的 impureFinalPropsSelectorFactory,其实和以前实现的 selectorFactory,差很少,无脑计算返回新值。

export function impureFinalPropsSelectorFactory( mapStateToProps, mapDispatchToProps, mergeProps, dispatch ) {
  return function impureFinalPropsSelector(state, ownProps) {
    return mergeProps(
      mapStateToProps(state, ownProps),
      mapDispatchToProps(dispatch, ownProps),
      ownProps
    )
  }
}
复制代码

怎么样,和以前本身实现的差很少吧,本身实现的还有一个浅对比呢~ 笑哭

pureFinalPropsSelectorFactory

export function pureFinalPropsSelectorFactory( mapStateToProps, mapDispatchToProps, mergeProps, dispatch, { areStatesEqual, areOwnPropsEqual, areStatePropsEqual } ) {
  let hasRunAtLeastOnce = false
  let state
  let ownProps
  let stateProps
  let dispatchProps
  let mergedProps

  function handleFirstCall(firstState, firstOwnProps) {
    state = firstState
    ownProps = firstOwnProps
    stateProps = mapStateToProps(state, ownProps)
    dispatchProps = mapDispatchToProps(dispatch, ownProps)
    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
    hasRunAtLeastOnce = true
    return mergedProps
  }

  function handleNewPropsAndNewState() {
    stateProps = mapStateToProps(state, ownProps)

    if (mapDispatchToProps.dependsOnOwnProps)
      dispatchProps = mapDispatchToProps(dispatch, ownProps)

    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
    return mergedProps
  }

  function handleNewProps() {
    // ...
  }

  function handleNewState() {
    // ...
  }

  function handleSubsequentCalls(nextState, nextOwnProps) {
    const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps)
    const stateChanged = !areStatesEqual(nextState, state)
    state = nextState
    ownProps = nextOwnProps

    if (propsChanged && stateChanged) return handleNewPropsAndNewState()
    if (propsChanged) return handleNewProps()
    if (stateChanged) return handleNewState()
    return mergedProps
  }

  return function pureFinalPropsSelector(nextState, nextOwnProps) {
    return hasRunAtLeastOnce
      ? handleSubsequentCalls(nextState, nextOwnProps)
      : handleFirstCall(nextState, nextOwnProps)
  }
}
复制代码

一句话: 在须要的时候才执行mapStateToProps,mapDispatchToProps,mergeProps 为了减小篇幅,挑部分讲解。其实这篇已经很长了有没有,不知道看到这里的你,犯困了没有? 仍是已经睡着了? 上个图醒醒脑

Anne Hathaway

言归正传,这里也分两种状况:

  • 当第一次运行时,执行handleFirstCall,将三个map函数运行一遍,并将结果缓存下来
  • 以后运行 handleSubsequentCalls,在其中将新的值和缓存的值作比较,若是变化,将从新求值并返回,若是没变化,返回缓存的旧值。

其中 mapFunction.dependsOnOwnProps 表明你传入的mapStateToProps是否使用了ownProps。 若是没有使用,那么props的变化将不会影响结果,换句话说对应的mapFunction将不会执行。 判断方法也很简单,就是获取function形参长度,如何得到呢? mdn function.length

总结

这边文章主要讲了react-redux使用方法以及分析了源码中 Provider、connect、selectorFactory。中间也穿插了一些demo方便你们理解。 到目前为止你们应该已经熟悉了整个框架的工做流程。因为感受篇幅过长,因此决定分为两期来说解。下面一期中主要是剩下的 connectHOC(connectAdvanced),这个才是react-redux的核心。 但愿看完的朋友没有浪费大家时间,有所帮助,有什么意见尽管提,就当大家本身是产品(🐶)好le.

个人github

写完又读了一遍,感受篇幅其实不长,想一想应该是本身写的累了。。。

react-redux源码分析及实现原型(下)

相关文章
相关标签/搜索