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

栏目: 编程工具 · 发布时间: 5年前

内容简介:redux作为大型应用的状态管理工具,如果想配合react使用,需要借助react-redux。 redux主要完成两件事情:那么,如果想要将react和redux搭配使用,就需要react组件可以根据redux中所存储的状态(store)更新view。 并且可以改变store。其实react-redux主要就是完成了这两件事情。 第一,通过将store传入root组件的context,使子节点可以获取到 state。 第二,通过store.subscribe 订阅store的变化,更新组件。 另外还有对

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

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

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

熟悉使用方法

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

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 a={123} />
  </Provider>,
  document.getElementById('root'));
复制代码

从上面的例子中我们可以看出,react-redux使用非常简单,仅仅使用了两个API, Providerconnect

  • 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 是如何工作的。

// 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 的

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 为了减少篇幅,挑部分讲解。其实这篇已经很长了有没有,不知道看到这里的你,犯困了没有? 还是已经睡着了? 上个图醒醒脑

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

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

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

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

总结

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

我的github

写完又读了一遍,感觉篇幅其实不长,想想应该是自己写的累了。。。


以上所述就是小编给大家介绍的《react-redux源码分析及实现原型(上)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

How to Build a Billion Dollar App

How to Build a Billion Dollar App

George Berkowski / Little, Brown Book Group / 2015-4-1 / USD 24.95

Apps have changed the way we communicate, shop, play, interact and travel and their phenomenal popularity has presented possibly the biggest business opportunity in history. In How to Build a Billi......一起来看看 《How to Build a Billion Dollar App》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具