Provider组件
Provider用于建立能够被子组件访问的全局属性,核心API有两个:
Provider简版源码实现:
import React from 'react'; import PropTypes from 'prop-types'; export default class Provider extends React.Component{ //指定子组件可以访问的属性类型 static childContextTypes = { store: PropTypes.object } constructor(props){ super(props); } //指定子组件可以访问的属性 getChildContext(){ return{ store: this.props.store } } render() { //用其包含的子组件进行视图渲染 return this.props.children; } }
然后,在引用到全局属性的子组件当中指定contextTypes进行指定的全局属性获取,同时需要在子组件的构造函数中声明context,否则context是一个空对象
static contextTypes = { store: PropTypes.object } constructor(props,context){ super(props,context); }
最后,把Provider组件作为根组件,传入相应相对应的全局属性即可:
<Provider store={store}> <App></App> </Provider>
connect函数
connect函数其实是一个代理模式的高阶组件,对目标组件进行增强或减弱形成一个新的组件,然后返回这个新的组件。
connect函数有两个参数,分别是mapStateToProps和mapDispatchToProps。
mapStateToProps
mapStateToProps是一个匿名函数,用于指定传递到组件当中的props,实现方式也简单,直接执行其匿名函数即可:
const stateProps = mapStateToProps(store.getState());
最后,把stateProps展开传进目标组件即可
mapDispatchToProps
mapDispatchToProps既可以是对象,也可以是函数,它主要是用于action和传进来的props进行关联,利用bindActionCreators函数对action进行了dispatch的封装,使action变成dispatch(action),形成派发指定的action对指定的props进行调用。
const dispatchProps = bindActionCreators(mapDispatchToProps,store.dispatch); //把action封装成dispatch(action) function bindActionCreators(actionCreators,dispatch) { return Object.keys(actionCreators).reduce((result,item) => { result[item] = (...args) => dispatch(actionCreators[item](...args)); return result; },{}) }
最后,把dispatchProps展开传进目标组件即可
connect函数的完整实现
import React from 'react'; import PropTypes from 'prop-types'; import {bindActionCreators} from './mini-redux'; export const connect = (mapStateToProps = state => state,mapDispatchToProps={}) => (WrapComponent) => { class ConnectComponent extends React.Component{ //指定要访问的全局属性,若contextTypes没有定义,context将是一个空对象 static contextTypes = { store: PropTypes.object } constructor(props,context){ super(props,context); this.state = { props: {} } this.update = this.update.bind(this); } componentDidMount(){ const {store} = this.context; store.subscribe(this.update);//订阅store里的状态,发生变化就调用update函数 this.update(); } componentWillUnmount(){ const {store} = this.context; store.unsubscribe(this.update);//移除监听stor状态变化的函数 } //获取mapStateToProps和mapDispatchToProps,放进this.state.props中更新组件 update(){ const {store} = this.context; const stateProps = mapStateToProps(store.getState());//获取传进来state const dispatchProps = bindActionCreators(mapDispatchToProps,store.dispatch);//把action封装成dispatch(action) this.setState({ props:{ ...this.state.props,...stateProps,...dispatchProps } }) } render(){ return( //把传递进来的state和封装后的dispatch(action)作为props传到目标组件 <WrapComponent {...this.state.props}></WrapComponent> ) } } return ConnectComponent; }
中间件
中间件利用applyMiddleWare来增强createStore函数,类似于装饰器模式,先来看看createStore函数的简版实现
createStore函数
export function createStore(reducer,enhancer) { if(enhancer){ //如果存在store增强器,就对createStore进行封装 return enhancer(createStore)(reducer); } let currentState = {};//保存项目当前的state,默认为空对象 let currentListeners = [];//用来存放监听store变化的函数 //用来获取当前的state function getState() { return currentState; } //派发action function dispatch(action) { currentState = reducer(currentState,action);//使用reducer处理当前派发过来的action currentListeners.forEach(currentListener => currentListener());//处理完成后返回新的state触发store的改变,遍历监听store变化的函数并执行使组件发生变化 return action; } //增加监听store变化的函数 function subscribe(listener) { currentListeners.push(listener); } //移除取消监听store变化的函数 function unsubscribe(listener) { currentListeners = currentListeners.filter(l => l !== listener) } dispatch({type:'xxxx'});//触发初次调用,type可以设任意值,但是必须要走default的case,获取默认值传给组件初始化 return {getState,dispatch,subscribe,unsubscribe};//返回store内部的三个函数供外部调用 }
applyMiddleWare函数
上面看到存在store增强器,其实也就是applyMiddleWare函数,返回两层嵌套的函数,主要是针对store的dispatch方法进行增强,让dispatch的action不是直接到达reducer函数进行处理,而是经过层层的中间件,所以applyMiddleWare函数简版实现如下:
function applyMiddleWare(...middlewares) { return createStore => (...args) => { const store = createStore(...args);//根据reducer和初始值创建原生store let dispatch = store.dispatch;//获取原生store的dispatch函数 //中间件是嵌套了两层函数的函数,接收getState和dispatch作为参数 const middlewareApi = { getState: store.getState,dispatch: (...args) => dispatch(...args) } const middlewareChain = middlewares.map(middleware => middleware(middlewareApi));//获取传递进来的中间件,并且初始化中间件函数 dispatch = compose(...middlewareChain)(store.dispatch);//组合多个中间件,并且把原生的dispatch放在作为最后一个中间件的dispatch参数 //单个中间件的做法:dispatch = middleware(middlewareApi)(store.dispatch); //返回经过增强dispatch函数的store return{ ...store,dispatch } } }
compose函数
compose函数是把多个中间件组合在一起,把compose(fn1,fn2,fn3)转换为fn1(fn2(fn3))的形式,函数从右往左执行,其实就是下一个函数执行结果作为前一个函数的参数,对应在这里就是把下一个中间件作为前一个中间函数的next参数,compose的简版源码如下:
function compose(...fns){ if(fns.length === 0){ return arg => arg } else if(fns.length === 1){ return fns[0] } else{ //返回结果:(...args) = > fn1(fn2(fn3(...args))) return fns.reduce((result,item) => (...args) => result(item(...args))); } }
thunk中间件简版实现
const thunk = ({dispatch,getState}) => next => action => { //如果action是一个函数,就执行该函数 if(typeof action === 'function'){ return action(dispatch,getState); } //否则,如果当前中间件不是最后一个中间件,就把action传递到下一个中间件中执行 //如果当前中间件已经是最后一个中间件,那么next就是store上原生的dispatch,直接给到reducer处理了 return next(action); } export default thunk;
定制中间件
我们尝试手动定制一个中间件arrayThunk,判断action是否为数组类型,如果为数组类型,就遍历数组,把数组中的元素作为action进行重新派发:
const arrayThunk = ({dispatch,getState}) => next => action => { if(Array.isArray(action)){ return action.forEach(item => dispatch(item)); } return next(action); } export default arrayThunk;
正常调用即可:
const store = createStore(count,applyMiddleWare(thunk,arrayThunk));