Redux是一種JavaScript的狀態(tài)管理容器,是一個(gè)獨(dú)立的狀態(tài)管理庫(kù),可配合其它框架使用,比如React。引入Redux主要為了使JavaScript中數(shù)據(jù)管理的方便,易追蹤,避免在大型的JavaScript應(yīng)用中數(shù)據(jù)狀態(tài)的使用混亂情況。Redux 試圖讓 state 的變化變得可預(yù)測(cè),為此做了一些行為限制約定,這些限制條件反映在 Redux 的三大原則中。
房山網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián),房山網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為房山成百上千家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設(shè)要多少錢,請(qǐng)找那個(gè)售后服務(wù)好的房山做網(wǎng)站的公司定做!
本文會(huì)介紹Redux的幾個(gè)基本概念和堅(jiān)持的三大原則,以及完整的回路一下Redux中的數(shù)據(jù)流。在了解以上這些概念之后,用自己的代碼來實(shí)現(xiàn)一個(gè)簡(jiǎn)版的Redux,并且用自己實(shí)現(xiàn)的Redux結(jié)合React框架,做一個(gè)簡(jiǎn)單的TodoList應(yīng)用示例。希望本文對(duì)于初識(shí)Redux的同學(xué)有一個(gè)清晰,全面的認(rèn)識(shí)。
Redux就是用來管理狀態(tài)數(shù)據(jù),所以第一個(gè)概念就是狀態(tài)數(shù)據(jù),state就是存放數(shù)據(jù)的地方,根據(jù)應(yīng)用需要,一般定義成一個(gè)對(duì)象,比如:
{ ????todos:?[], ????showType:?'ALL', ????lastUpdate:?'2019-10-30?11:56:11' }
?
web應(yīng)用,所有的數(shù)據(jù)狀態(tài)變更,都是由一個(gè)行為觸發(fā)的,比如用戶點(diǎn)擊,網(wǎng)絡(luò)加載完成,或者定時(shí)事件。在簡(jiǎn)單應(yīng)用里面,我們一般都是在行為觸發(fā)的時(shí)候,直接修改對(duì)應(yīng)的數(shù)據(jù)狀態(tài),但是在大型復(fù)雜的應(yīng)用里面,修改同一數(shù)據(jù)的地方可能很多,每個(gè)地方直接修改,會(huì)造成數(shù)據(jù)狀態(tài)不可維護(hù)。
Redux引入了action的概念,每個(gè)要改變數(shù)據(jù)狀態(tài)的行為,都定義成一個(gè)action對(duì)象,用一個(gè)type來標(biāo)志是什么行為,行為附帶的數(shù)據(jù),也都直接放在action對(duì)象,比如一個(gè)用戶輸入的行為:
{ ????type:?'INPUT_TEXT', ????text:?'今天下午6點(diǎn)活動(dòng)碰頭會(huì)議' }
然后通過dispatch觸發(fā)這個(gè)action,dispatch(action)
狀態(tài),action的概念了解了,當(dāng)action觸發(fā)的時(shí)候,肯定要修改state數(shù)據(jù),在講解action的時(shí)候有說過,不能直接修改state,我們需要定義一個(gè)reducer來修改數(shù)據(jù),這個(gè)reducer就是一個(gè)行為響應(yīng)函數(shù),他接收當(dāng)前state,和對(duì)應(yīng)的action對(duì)象,根據(jù)不同的action,做相應(yīng)的邏輯判斷和數(shù)據(jù)處理,然后返回一個(gè)新的state。
注意,一定是返回一個(gè)新的state,不能直接修改參數(shù)傳入的原state,這是redux的原則之一,后面會(huì)講到。
function?reducer?(?state?=?[],?action?)?{ ????switch?(?action.type?)?{ ????????case?'INPUT_TEXT': ????????????return?[...state,?{text:?action.text,?id:?Math.random()?}] ????????default: ????????????return?state; ????} }
?
數(shù)據(jù)的更新已經(jīng)在reducer中完成了,在一些響應(yīng)式的web應(yīng)用中,我們往往需要監(jiān)聽數(shù)據(jù)狀態(tài)的變化,這個(gè)時(shí)候就可以用subscribe了
redux內(nèi)部保存一個(gè)監(jiān)聽隊(duì)列,listeners,可以調(diào)用subscribe來往listeners里面增加新的監(jiān)聽函數(shù),每次reducer修改完state之后,會(huì)逐個(gè)執(zhí)行監(jiān)聽函數(shù),而監(jiān)聽函數(shù)可以獲取已經(jīng)更新過的state數(shù)據(jù)了
listeners?=?[]; subscrible(?listener?)?{ ????listeners.push(?listener?); ????return?function?()?{ ????????let?index?=?listeners.index(?listener?); ????????listeners.splice(?index,?1?); ????} } dispatch(?action?)?//?觸發(fā)?action reducer(state,?action) listeners.map(?(?listener?)?=>?{ ????listener() }?)
?
整個(gè)應(yīng)用的數(shù)據(jù)都在state,并且只有這一個(gè)state,這么做的目的是方便管理,整個(gè)應(yīng)用的數(shù)據(jù)就這一份,調(diào)試方便,開發(fā)也方便,可以在開發(fā)的時(shí)候用本地的數(shù)據(jù)。而且開發(fā)同構(gòu)應(yīng)用也很方便,比如服務(wù)端渲染,把服務(wù)端的數(shù)據(jù)全部放在state,作為web端初始化時(shí)候的數(shù)據(jù)
state的數(shù)據(jù)對(duì)外只讀,不能直接修改state,唯一可以修改的方式是觸發(fā)action,然后通過reducer來處理。
因?yàn)樗械男薷亩急患谢幚?,且?yán)格按照一個(gè)接一個(gè)的順序執(zhí)行,因此不用擔(dān)心競(jìng)態(tài)條件(race?condition)的出現(xiàn)。?Action?就是普通對(duì)象而已,因此它們可以被日志打印、序列化、儲(chǔ)存、后期調(diào)試或測(cè)試時(shí)回放出來。
先說明下什么是純函數(shù),純函數(shù)指的是函數(shù)內(nèi)部不修改傳入的參數(shù),無副作用,在傳參一定的情況下,返回的結(jié)果也是一定的。Redux中的Reducer需要設(shè)計(jì)成存函數(shù),不能直接操作傳入的state,需要把改變的數(shù)據(jù)以一個(gè)新的state方式返回。
其實(shí)上面講Redux基本概念的時(shí)候已經(jīng)大概的說了下數(shù)據(jù)流向方式了,就是: view->action->reducer->state->view,用文字來表述就是,首先由于頁(yè)面上的某些事件會(huì)觸發(fā)action,通過dispatch(action)來實(shí)現(xiàn),然后通過reducer處理,reducer(state, action)返回一個(gè)新的state,完成state的更新,當(dāng)然對(duì)于響應(yīng)式的應(yīng)用,會(huì)觸發(fā)listener(),在listener里面獲取最新的state狀態(tài),完成對(duì)應(yīng)視圖(view)的更新。這就是整個(gè)redux中的數(shù)據(jù)流描述,如下圖所示:
在對(duì)Redux的基本概念和幾大原則熟悉了之后,可以實(shí)現(xiàn)一個(gè)自己的Redux了,當(dāng)然我們一般都直接用官方的npm包,這里自己實(shí)現(xiàn)的比較簡(jiǎn)單,沒有做什么入?yún)Ⅱ?yàn)證,異常處理之類的,主要是加深下對(duì)Redux的理解。下面直接貼代碼了,對(duì)應(yīng)的概念都有注釋。
//?redux.js //?創(chuàng)建state的函數(shù) //?傳入reducer?和初始化的state function?createStore(?reducer,?initState?)?{ ????let?ref?=?{}; ????let?listeners?=?[]; ????let?currentState?=?initState; ????//?dispath函數(shù),用來觸發(fā)action ????function?dispatch?(?action?)?{ ????????//?觸發(fā)的action,通過reducer處理 ????????currentState?=?reducer(?currentState,?action?) ????????//?處理完成后,通知listeners ????????for?(?let?i?in?listeners?)?{ ????????????let?listener?=?listener[?i?]; ????????????listener(); ????????} ????????return?action; ????} ????//?返回當(dāng)前的state ????function?getState?()?{ ????????return?currentState; ????} ????//?訂閱state變化,?傳入listener,返回取消訂閱的function ????function?subscribe?(?listener?)?{ ????????listeners.push(?listener?); ????????return?function?()?{ ????????????let?index?=?listeners.indexOf(?listener?); ????????????if?(?index?>?-1?)?{ ????????????????listeners.splice(?index,?1?); ????????????} ????????} ????} ???? ????ref?=?{ ????????dispatch:?dispatch, ????????subscribe:?subscribe, ????????getState:?getState ????}; ????return?ref; } function?combineReducers(?reducers?)?{ ????return?function?(?state,?action?)?{ ????????let?finalState?=?{}; ????????let?hasChanged?=?false; ????????for?(?let?key?in?reducers?)?{ ????????????let?reducer?=?reducers[?key?] ????????????if?(?typeof?reducer?===?'function'?)?{ ????????????????let?keyState?=?reducer(?state?&&?state[?key?],?action?); ????????????????hasChanged?=?hasChanged?||?keyState?!==?state[?key?]; ????????????????finalState[?key?]?=?keyState; ????????????} ????????} ????????return?hasChanged???finalState?:?state; ????} } export?{?createStore,?combineReducers?}
是不是覺得怎么才這么點(diǎn)代碼,就是這么點(diǎn)代碼,而且還包含了一個(gè)combineReducers輔助函數(shù),下面再貼一點(diǎn)使用示例代碼
//?reducer函數(shù),用于處理action function?reducer(?state?=?[],?action?)?{ ????switch(?action.type?)?{ ????????case?'INPUT_TEXT': ????????????return?[?...state,?{?text:?action.text,?key:?Math.random(),?isDo:?false?}]; ????????case?'TOGGLE_TODO': ????????????return?state.map(?(?item?)?=>?{ ????????????????if?(?item.key?===?action.id?)?{ ????????????????????return?{...item,?isDo:?!item.isDo?}; ????????????????} ????????????}?); ????????default: ????????????return?state; ????} } let?store?=?createStore(?reducer?); //?在用戶輸入一條Todo時(shí)候 console.log(store.getState()); store.dispatch(?{?type:?'INPUT_TEXT',?text:?'這里是一條待辦事項(xiàng)'?}?); console.log(store.getState()); //在用戶點(diǎn)擊一條Todo?Item的時(shí)候,切換完成狀態(tài) console.log(store.getState()); store.dispatch(?{?type:?'TOGGLE_TODO',?id:?item.key?}?) console.log(store.getState());
?
下面,利用Redux結(jié)合React開發(fā)一個(gè)簡(jiǎn)單的Todo工具,頁(yè)面主要功能點(diǎn)
1、可以添加Todo事項(xiàng)
2、點(diǎn)擊事項(xiàng)會(huì)切換事項(xiàng)的完成狀態(tài)
3、可以切換展示全部/已完成/待完成事項(xiàng)
這個(gè)實(shí)例是基于react,react-redux完成的,項(xiàng)目搭建用的是create-react-app,利用react-redux提供的接口,將redux中的state和action集成到組件中,需要讀者熟悉create-react-app的使用,以及react-redux的主要接口功能,以下貼出主要代碼,感興趣的同學(xué)可以自己搭建實(shí)現(xiàn)
首先定義好state數(shù)據(jù)結(jié)構(gòu)和action以及對(duì)應(yīng)的reducer
state包含兩部分,一是todos,待辦事項(xiàng)列表,二是showType,展示類型
action包含這么三種,一是添加新的Todo,二是切換事項(xiàng)完成狀態(tài),三是切換展示類型,分別定義好
actions.js
//?actions.js let?nextTodoId?=?0 export?const?addTodo?=?text?=>?{ ????return?{ ????????type:?'ADD_TODO', ????????id:?nextTodoId++, ????????text ????}; }; export?const?setShowType?=?showType?=>?{ ????return?{ ????????type:?"SET_SHOW_TYPE", ????????showType ????}; }; export?const?toggleTodo?=?id?=>?{ ????return?{ ????????type:?'TOGGLE_TODO', ????????id ????}; };
reducers.js
const?todos?=?(?state?=?[],?action?)?=>?{ ????switch?(?action.type?)?{ ????????case?'ADD_TODO': ????????????return?[ ????????????????...state, ????????????????{ ????????????????????id:?action.id, ????????????????????text:?action.text, ????????????????????isDo:?false ????????????????} ????????????]; ????????case?'TOGGLE_TODO': ????????????return?state.map(?todo?=>?{ ????????????????return?todo.id?===?action.id???{...todo,?isDo:?!todo.isDo?}?:?todo; ????????????}?); ????????default: ????????????return?state; ????} } const?showType?=?(?state?=?'SHOW_ALL',?action?)?=>?{ ????switch?(?action.type?)?{ ????????case?'SET_SHOW_TYPE': ????????????return?action.showType; ????????default: ????????????return?state; ????} } const?todoList?=?combineReducers({ ????todos, ????showType }) export?{?todoList?}
?
至此,數(shù)據(jù)狀態(tài)redux部分算完成了,接下來實(shí)現(xiàn)對(duì)應(yīng)的Component和入口文件了,準(zhǔn)備分這么幾個(gè)組件
1、待辦事項(xiàng)Todo
2、輸入框 AddTodo
3、待辦事項(xiàng)列表TodoList
4、底部展示類型切換Tab
//?component.js import?{?connnect?}?from?'react-redux'; import?{?addTodo,?setShowType,?toggleTodo?}?from?'./actions' const?Todo?=?(?{?onClick,?completed,?text?}?)?=>?( ????
????????Show:?{?'?'?}
????????
?
入口文件 index.js
import?React?from?'react'; import?ReactDOM?from?'react-dom'; import?{?Provider?}?from?'react-redux'; import?{?createStore?}?from?'./redux'; import?todoList?from?'./reducers' import?{AddTodo,?ShowTodoList,?Tab?}?from?'./component' let?store?=?createStore(?todoApp?); ReactDOM.render( ???????????? ????,?document.getElementById('root'));???????????????????????????? ???????????? ????????
?
主要代碼完成,npm start 運(yùn)行,功能截圖如下
文章同步發(fā)布:?https://www.geek-share.com/detail/2783420870.html
參考文章:
原生實(shí)現(xiàn)一個(gè)react-redux的代碼示例
用React實(shí)現(xiàn)一個(gè)完整的TodoList的示例代碼