内容简介: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, Provider
与 connect
。
- 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 为了减少篇幅,挑部分讲解。其实这篇已经很长了有没有,不知道看到这里的你,犯困了没有? 还是已经睡着了? 上个图醒醒脑
言归正传,这里也分两种情况:
- 当第一次运行时,执行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.
写完又读了一遍,感觉篇幅其实不长,想想应该是自己写的累了。。。
以上所述就是小编给大家介绍的《react-redux源码分析及实现原型(上)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。