react-redux简版实现
Provider組件
Provider用于建立能夠被子組件訪問的全局屬性,核心API有兩個:
- childContextTypes靜態(tài)屬性,用于指定被子組件訪問的全局屬性類型
- getChildContext方法,用于指定可以被子組件訪問的全局屬性
Provider簡版源碼實現(xiàn):
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進行指定的全局屬性獲取,同時需要在子組件的構(gòu)造函數(shù)中聲明context,否則context是一個空對象
static contextTypes = {store: PropTypes.object }constructor(props,context){super(props,context); }最后,把Provider組件作為根組件,傳入相應(yīng)相對應(yīng)的全局屬性即可:
<Provider store={store}><App></App> </Provider>connect函數(shù)
connect函數(shù)其實是一個代理模式的高階組件,對目標組件進行增強或減弱形成一個新的組件,然后返回這個新的組件。
connect函數(shù)有兩個參數(shù),分別是mapStateToProps和mapDispatchToProps。
mapStateToProps
mapStateToProps是一個匿名函數(shù),用于指定傳遞到組件當中的props,實現(xiàn)方式也簡單,直接執(zhí)行其匿名函數(shù)即可:
const stateProps = mapStateToProps(store.getState());最后,把stateProps展開傳進目標組件即可
mapDispatchToProps
mapDispatchToProps既可以是對象,也可以是函數(shù),它主要是用于action和傳進來的props進行關(guān)聯(lián),利用bindActionCreators函數(shù)對action進行了dispatch的封裝,使action變成dispatch(action),形成派發(fā)指定的action對指定的props進行調(diào)用。
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函數(shù)的完整實現(xiàn)
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里的狀態(tài),發(fā)生變化就調(diào)用update函數(shù)this.update();}componentWillUnmount(){const {store} = this.context;store.unsubscribe(this.update);//移除監(jiān)聽stor狀態(tài)變化的函數(shù)}//獲取mapStateToProps和mapDispatchToProps,放進this.state.props中更新組件update(){const {store} = this.context;const stateProps = mapStateToProps(store.getState());//獲取傳進來stateconst 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函數(shù),類似于裝飾器模式,先來看看createStore函數(shù)的簡版實現(xiàn)
createStore函數(shù)
export function createStore(reducer,enhancer) {if(enhancer){//如果存在store增強器,就對createStore進行封裝return enhancer(createStore)(reducer);}let currentState = {};//保存項目當前的state,默認為空對象let currentListeners = [];//用來存放監(jiān)聽store變化的函數(shù)//用來獲取當前的statefunction getState() {return currentState;}//派發(fā)actionfunction dispatch(action) {currentState = reducer(currentState,action);//使用reducer處理當前派發(fā)過來的actioncurrentListeners.forEach(currentListener => currentListener());//處理完成后返回新的state觸發(fā)store的改變,遍歷監(jiān)聽store變化的函數(shù)并執(zhí)行使組件發(fā)生變化return action;}//增加監(jiān)聽store變化的函數(shù)function subscribe(listener) {currentListeners.push(listener);}//移除取消監(jiān)聽store變化的函數(shù)function unsubscribe(listener) {currentListeners = currentListeners.filter(l => l !== listener)}dispatch({type:'xxxx'});//觸發(fā)初次調(diào)用,type可以設(shè)任意值,但是必須要走default的case,獲取默認值傳給組件初始化return {getState,dispatch,subscribe,unsubscribe};//返回store內(nèi)部的三個函數(shù)供外部調(diào)用 }applyMiddleWare函數(shù)
上面看到存在store增強器,其實也就是applyMiddleWare函數(shù),返回兩層嵌套的函數(shù),主要是針對store的dispatch方法進行增強,讓dispatch的action不是直接到達reducer函數(shù)進行處理,而是經(jīng)過層層的中間件,所以applyMiddleWare函數(shù)簡版實現(xiàn)如下:
function applyMiddleWare(...middlewares) {return createStore => (...args) => {const store = createStore(...args);//根據(jù)reducer和初始值創(chuàng)建原生storelet dispatch = store.dispatch;//獲取原生store的dispatch函數(shù)//中間件是嵌套了兩層函數(shù)的函數(shù),接收getState和dispatch作為參數(shù)const middlewareApi = {getState: store.getState,dispatch: (...args) => dispatch(...args)}const middlewareChain = middlewares.map(middleware => middleware(middlewareApi));//獲取傳遞進來的中間件,并且初始化中間件函數(shù)dispatch = compose(...middlewareChain)(store.dispatch);//組合多個中間件,并且把原生的dispatch放在作為最后一個中間件的dispatch參數(shù)//單個中間件的做法:dispatch = middleware(middlewareApi)(store.dispatch);//返回經(jīng)過增強dispatch函數(shù)的storereturn{...store,dispatch}} }compose函數(shù)
compose函數(shù)是把多個中間件組合在一起,把compose(fn1,fn2,fn3)轉(zhuǎn)換為fn1(fn2(fn3))的形式,函數(shù)從右往左執(zhí)行,其實就是下一個函數(shù)執(zhí)行結(jié)果作為前一個函數(shù)的參數(shù),對應(yīng)在這里就是把下一個中間件作為前一個中間函數(shù)的next參數(shù),compose的簡版源碼如下:
function compose(...fns){if(fns.length === 0){return arg => arg}else if(fns.length === 1){return fns[0]}else{//返回結(jié)果:(...args) = > fn1(fn2(fn3(...args)))return fns.reduce((result,item) => (...args) => result(item(...args)));} }thunk中間件簡版實現(xiàn)
const thunk = ({dispatch,getState}) => next => action => {//如果action是一個函數(shù),就執(zhí)行該函數(shù)if(typeof action === 'function'){return action(dispatch,getState);}//否則,如果當前中間件不是最后一個中間件,就把action傳遞到下一個中間件中執(zhí)行//如果當前中間件已經(jīng)是最后一個中間件,那么next就是store上原生的dispatch,直接給到reducer處理了return next(action); }export default thunk;定制中間件
我們嘗試手動定制一個中間件arrayThunk,判斷action是否為數(shù)組類型,如果為數(shù)組類型,就遍歷數(shù)組,把數(shù)組中的元素作為action進行重新派發(fā):
const arrayThunk = ({dispatch,getState}) => next => action => {if(Array.isArray(action)){return action.forEach(item => dispatch(item));}return next(action); }export default arrayThunk;正常調(diào)用即可:
const store = createStore(count,applyMiddleWare(thunk,arrayThunk));總結(jié)
以上是生活随笔為你收集整理的react-redux简版实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ps怎么设计游戏手柄logo标志? Ph
- 下一篇: React+Redux仿Web追书神器