You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
我们一起来研究下当前最流行的React状态管理容器Redux,本文基于Redux v3.7.2 ,Redux用一个单独的状态树对象(state tree object)保存整个应用的状态,这个对象不能直接被改变(immutable),当数据变化了,一个新的对象就会被创建(通过actions和reducers)
Redux有如下优点:
可预测
始终有一个唯一的准确的数据源(single source of truth),就是store,通过actions和reducers来保证整个应用状态同步,做到绝不混乱
exportdefaultfunctionwarning(message){if(typeofconsole!=='undefined'&&typeofconsole.error==='function'){console.error(message)}try{// This error was thrown as a convenience so that if you enable// "break on all exceptions" in your console,// it would pause the execution at this line.thrownewError(message)}catch(e){}}
index.js
入口文件index.js主要用于引出公共方法API供外面调用,isCrushed那段用于非生产环境
importcreateStorefrom'./createStore'importcombineReducersfrom'./combineReducers'importbindActionCreatorsfrom'./bindActionCreators'importapplyMiddlewarefrom'./applyMiddleware'importcomposefrom'./compose'importwarningfrom'./utils/warning'functionisCrushed(){}if(process.env.NODE_ENV!=='production'&&typeofisCrushed.name==='string'&&isCrushed.name!=='isCrushed'){warning('You are currently using minified code outside of NODE_ENV === \'production\'. '+'This means that you are running a slower development build of Redux. '+'You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify '+'or DefinePlugin for webpack (http://stackoverflow.com/questions/30030031) '+'to ensure you have the correct code for your production build.')}export{createStore,combineReducers,bindActionCreators,applyMiddleware,compose}
createStore.js
createStore.js用于Store的生成,接受3个参数:
reducer函数,接受当前state tree和一个action,返回新的state tree
preloadedState,初始状态树,如果你使用combineReducers,必须确保初始state tree object的keys和combineReducers的keys保持一致
enhancer,用来强化store,比如middleware / time travel,Redux自带的唯一enhancer是applyMiddleware()
importisPlainObjectfrom'lodash/isPlainObject'import$$observablefrom'symbol-observable'exportconstActionTypes={INIT: '@@redux/INIT'}exportdefaultfunctioncreateStore(reducer,preloadedState,enhancer){if(typeofpreloadedState==='function'&&typeofenhancer==='undefined'){enhancer=preloadedStatepreloadedState=undefined}if(typeofenhancer!=='undefined'){if(typeofenhancer!=='function'){thrownewError('Expected the enhancer to be a function.')}returnenhancer(createStore)(reducer,preloadedState)}if(typeofreducer!=='function'){thrownewError('Expected the reducer to be a function.')}letcurrentReducer=reducerletcurrentState=preloadedStateletcurrentListeners=[]letnextListeners=currentListenersletisDispatching=falsefunctionensureCanMutateNextListeners(){if(nextListeners===currentListeners){nextListeners=currentListeners.slice()}}functiongetState(){returncurrentState}functionsubscribe(listener){if(typeoflistener!=='function'){thrownewError('Expected listener to be a function.')}letisSubscribed=trueensureCanMutateNextListeners()nextListeners.push(listener)returnfunctionunsubscribe(){if(!isSubscribed){return}isSubscribed=falseensureCanMutateNextListeners()constindex=nextListeners.indexOf(listener)nextListeners.splice(index,1)}}functiondispatch(action){if(!isPlainObject(action)){thrownewError('Actions must be plain objects. '+'Use custom middleware for async actions.')}if(typeofaction.type==='undefined'){thrownewError('Actions may not have an undefined "type" property. '+'Have you misspelled a constant?')}if(isDispatching){thrownewError('Reducers may not dispatch actions.')}try{isDispatching=truecurrentState=currentReducer(currentState,action)}finally{isDispatching=false}constlisteners=currentListeners=nextListenersfor(leti=0;i<listeners.length;i++){constlistener=listeners[i]listener()}returnaction}functionreplaceReducer(nextReducer){if(typeofnextReducer!=='function'){thrownewError('Expected the nextReducer to be a function.')}currentReducer=nextReducerdispatch({type: ActionTypes.INIT})}functionobservable(){constouterSubscribe=subscribereturn{subscribe(observer){if(typeofobserver!=='object'){thrownewTypeError('Expected the observer to be an object.')}functionobserveState(){if(observer.next){observer.next(getState())}}observeState()constunsubscribe=outerSubscribe(observeState)return{ unsubscribe }},[$$observable](){returnthis}}}dispatch({type: ActionTypes.INIT})return{
dispatch,
subscribe,
getState,
replaceReducer,[$$observable]: observable}}
functionbindActionCreator(actionCreator,dispatch){return(...args)=>dispatch(actionCreator(...args))}exportdefaultfunctionbindActionCreators(actionCreators,dispatch){if(typeofactionCreators==='function'){returnbindActionCreator(actionCreators,dispatch)}if(typeofactionCreators!=='object'||actionCreators===null){thrownewError(`bindActionCreators expected an object or a function, instead received ${actionCreators===null ? 'null' : typeofactionCreators}. `+`Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`)}constkeys=Object.keys(actionCreators)constboundActionCreators={}for(leti=0;i<keys.length;i++){constkey=keys[i]constactionCreator=actionCreators[key]if(typeofactionCreator==='function'){boundActionCreators[key]=bindActionCreator(actionCreator,dispatch)}}returnboundActionCreators}
combineReducers.js
用来合并Reducers,将多个Reducer函数构成的对象转换为单个Reducer函数,当应用较大时Reducers可以按照模块拆分(domain model && SRP),代码结构比较清晰,它会调用每个子Reducer,并收集结果组成一个单一的state tree object,其keys与传递的子Reducer的keys相对应
对每一个Reducer,其初始state不可以为undefined,但可以为null,对传入的action,如果action为undefined,应该返回初始state,如果是未知的action,应该返回当前state
conststore=createStore(reducer,applyMiddleware(…middlewares))// orconststore=createStore(reducer,{},applyMiddleware(…middlewares))exportdefaultfunctioncreateStore(reducer,preloadedState,enhancer){if(typeofpreloadedState==='function'&&typeofenhancer==='undefined'){enhancer=preloadedStatepreloadedState=undefined}if(typeofenhancer!=='undefined'){if(typeofenhancer!=='function'){thrownewError('Expected the enhancer to be a function.')}returnenhancer(createStore)(reducer,preloadedState)}}
介绍
我们一起来研究下当前最流行的React状态管理容器Redux,本文基于Redux v3.7.2 ,Redux用一个单独的状态树对象(state tree object)保存整个应用的状态,这个对象不能直接被改变(immutable),当数据变化了,一个新的对象就会被创建(通过actions和reducers)
Redux有如下优点:
始终有一个唯一的准确的数据源(single source of truth),就是store,通过actions和reducers来保证整个应用状态同步,做到绝不混乱
具备可预测的结果和严格的组织结构让代码更容易维护
编写可测试代码的首要准则是编写可以仅做一件事并且独立的小函数(single responsibility principle),Redux的代码几乎全部都是这样的函数:短小·纯粹·分离
实例代码
举个小栗子🌰看看应用代码中如何使用Redux
例子取自Redux examples 里面的counter
整体结构
utils/warning.js
utils目录下面的warning.js负责控制台错误日志的输出,用于非生产环境下(
process.env.NODE_ENV !== 'production'
)抛出错误便于debug:index.js
入口文件index.js主要用于引出公共方法API供外面调用,isCrushed那段用于非生产环境
createStore.js
createStore.js用于Store的生成,接受3个参数:
combineReducers
,必须确保初始state tree object的keys和combineReducers
的keys保持一致applyMiddleware()
getState()
用来返回当前state tree
replaceReducer(nextReducer)
用来替换掉当前store用的reducer,可用于动态按需加载或者热替换等场景
subscribe(listener)
订阅函数,用来注册监听事件,并返回取消订阅的函数
每当dispatch一个action时注册的listener被调用,为实现实时性,监听函数listener加入到nextListeners数组中,而dispatch事件使用currentListeners数组
dispatch(action)
用来分发action修改state tree的唯一方式,action必须是plain object并带有type属性
observable()
用于与
observable/reactive lib
互操作,当dispatch一个action时,调用注册的observer的next方法bindActionCreators.js
将action creators转换成具有同名keys的对象,用dispatch把每个action creator包起来,这样就可以直接调用它们,将包好的action creator往下传到一个组件上,却不让这个组件觉察到Redux的存在,松耦合
combineReducers.js
用来合并Reducers,将多个Reducer函数构成的对象转换为单个Reducer函数,当应用较大时Reducers可以按照模块拆分(domain model && SRP),代码结构比较清晰,它会调用每个子Reducer,并收集结果组成一个单一的state tree object,其keys与传递的子Reducer的keys相对应
对每一个Reducer,其初始state不可以为undefined,但可以为null,对传入的action,如果action为undefined,应该返回初始state,如果是未知的action,应该返回当前state
compose.js
用来组合传入的多个函数,在中间件时会用到,最终结果是把各个函数从右至左串联起来,相当于:
compose(a,b) = (...args) => a(b(...args))
applyMiddleware.js
用于强化Store
上面👆分析createStore.js我们知道,中间件可以作为createStore的第二个或者第三个参数传入,如果有中间件,执行结果相当于
applyMiddleware(…middlewares)(createStore)(reducer, preloadedState)
从下面👇的代码可以看到,相当于用compose把各个middleware串联起来,传入dispatch得到更新的dispatch
为了串联所有middleware,其接受三层参数,第一层是Store,第二层是下一个middleware,第三层才是action,代码如下:
问:
middlewareAPI
中的dispatch
为何用匿名函数包起来?答:经过
applyMiddleware()
之后的dispatch
是更新过的,包起来后,只要dispatch
更新,middlewareAPI
中的dispatch
也会变化举个栗子🌰,"臭名昭著"的
Thunk
GitHub repo for Redux
The text was updated successfully, but these errors were encountered: