React 技術(shù)棧學(xué)習(xí)谋梭,redux 知識(shí)點(diǎn)

如何合理地設(shè)計(jì) state

  • 象設(shè)計(jì)數(shù)據(jù)庫(kù)一樣設(shè)計(jì) state锈拨,state 中的每一部分狀態(tài)看作數(shù)據(jù)庫(kù)中的一張表十拣。
  • 把整個(gè)應(yīng)用的狀態(tài)按照領(lǐng)域(Domain)分成若干子 state叙身,子 state 之間不能保存重復(fù)的數(shù)據(jù)渔扎。
  • state 以鍵值對(duì)的結(jié)構(gòu)存儲(chǔ)數(shù)據(jù),以記錄的 key/ID 作為記錄的索引信轿,記錄中的其他字段都依賴(lài)于索引(即類(lèi)似數(shù)據(jù)庫(kù)表中的主鍵)晃痴。
  • state 中不能保存可以通過(guò)已有數(shù)據(jù)計(jì)算而來(lái)的數(shù)據(jù),即 state 中的字段不互相依賴(lài)财忽。
  • 一個(gè)狀態(tài)節(jié)點(diǎn)只屬于一個(gè)模塊愧旦。
  • 盡量避免冗余數(shù)據(jù)。
  • 樹(shù)形結(jié)構(gòu)扁平化定罢,層次盡量不要太深笤虫,最好保持在三層以?xún)?nèi)。

設(shè)計(jì) state 總結(jié)

設(shè)計(jì) Redux State 的關(guān)鍵在于祖凫,像設(shè)計(jì)數(shù)據(jù)庫(kù)一樣設(shè)計(jì) state琼蚯。把 state 看作應(yīng)用在內(nèi)存中的一個(gè)數(shù)據(jù)庫(kù),action惠况、reducer 等看作操作這個(gè)數(shù)據(jù)庫(kù)的 SQL 語(yǔ)句遭庶。

state 的 key

  • createStore 的參數(shù) reducer 的 key 即為 store 樹(shù)上的 key,類(lèi)似表名稠屠,在 reducer 里僅能操作 reducer 的 key 對(duì)應(yīng)的 store峦睡,不能操作別的 key 下的數(shù)據(jù),所以权埠,也不用給前綴榨了。
  • 而在 mapStateToProps 函數(shù)里,是需要寫(xiě) key攘蔽,即需要指定“表名”的龙屉。

redux 的三大原則

參考

單一數(shù)據(jù)源

  • 整個(gè)應(yīng)用的 state 被儲(chǔ)存在一棵 object tree 中,并且這個(gè) object tree 只存在于唯一一個(gè) store 中满俗。
  • 這讓同構(gòu)應(yīng)用開(kāi)發(fā)變得非常容易转捕。來(lái)自服務(wù)端的 state 可以在無(wú)需編寫(xiě)更多代碼的情況下被序列化并注入到客戶(hù)端中作岖。由于是單一的 state tree ,調(diào)試也變得非常容易五芝。在開(kāi)發(fā)中痘儡,你可以把應(yīng)用的 state 保存在本地,從而加快開(kāi)發(fā)速度枢步。此外谤辜,受益于單一的 state tree ,以前難以實(shí)現(xiàn)的如“撤銷(xiāo)/重做”這類(lèi)功能也變得輕而易舉价捧。

state 是只讀的

  • 唯一改變 state 的方法就是觸發(fā) action,action 是一個(gè)用于描述已發(fā)生事件的普通對(duì)象涡戳。
  • 這樣確保了視圖和網(wǎng)絡(luò)請(qǐng)求都不能直接修改 state结蟋,相反它們只能表達(dá)想要修改的意圖。因?yàn)樗械男薷亩急患谢幚碛嬲茫覈?yán)格按照一個(gè)接一個(gè)的順序執(zhí)行嵌屎,因此不用擔(dān)心 race condition 的出現(xiàn)。 Action 就是普通對(duì)象而已恍涂,因此它們可以被日志打印宝惰、序列化、儲(chǔ)存再沧、后期調(diào)試或測(cè)試時(shí)回放出來(lái)尼夺。

使用純函數(shù)來(lái)執(zhí)行修改

  • 為了描述 action 如何改變 state tree ,你需要編寫(xiě) reducers炒瘸。
  • Reducer 只是一些純函數(shù)淤堵,它接收先前的 state 和 action,并返回新的 state顷扩。剛開(kāi)始你可以只有一個(gè) reducer拐邪,隨著應(yīng)用變大,你可以把它拆成多個(gè)小的 reducers隘截,分別獨(dú)立地操作 state tree 的不同部分扎阶,因?yàn)?reducer 只是函數(shù),你可以控制它們被調(diào)用的順序婶芭,傳入附加數(shù)據(jù)东臀,甚至編寫(xiě)可復(fù)用的 reducer 來(lái)處理一些通用任務(wù),如分頁(yè)器犀农。

Redux 三個(gè)基本概念

action

  1. action 是把數(shù)據(jù)從應(yīng)用層傳遞到 store 的有效載體啡邑,它是 store 數(shù)據(jù)的唯一來(lái)源。
  2. action 本質(zhì)上是 JavaScript 普通對(duì)象井赌。
  3. 我們約定谤逼,action 內(nèi)必須使用一個(gè)字符串類(lèi)型的 type 字段來(lái)表示將要執(zhí)行的動(dòng)作瑰剃。
  4. action 僅僅表示某對(duì)象發(fā)生了什么行為,我們應(yīng)該盡量減少在 action 中傳遞的數(shù)據(jù)脊另。

reducer

  1. action 只是描述了有事情發(fā)生了這一事實(shí)塘娶,并沒(méi)有指明應(yīng)用如何更新 state。
  2. 而這正是 reducer 要做的事情枝冀。reducer 就是一個(gè)純函數(shù)舞丛,接收當(dāng)前 state 和 action,返回新的 state果漾。函數(shù)形式:(previousState, action) => newState
  3. 只要傳入?yún)?shù)相同球切,返回的 newState 就一定相同。沒(méi)有特殊情況绒障、沒(méi)有副作用吨凑,沒(méi)有 API 請(qǐng)求、沒(méi)有變量修改户辱,單純執(zhí)行計(jì)算鸵钝。
  4. 保持 reducer 純凈非常重要。永遠(yuǎn)不要在 reducer 里做這些操作:
    • 修改傳入?yún)?shù)庐镐;
    • 執(zhí)行有副作用的操作恩商,如 API 請(qǐng)求和路由跳轉(zhuǎn);
    • 調(diào)用非純函數(shù)必逆,如 Date.now() 或 Math.random()怠堪。
  5. 記得不要修改 previousState 的值,創(chuàng)建一個(gè)新的對(duì)象返回給 newState名眉。

store

使用 reducers 來(lái)根據(jù) action 更新 state, 存儲(chǔ)在 store 中研叫。store 把之前創(chuàng)建的 action 和 reducer 聯(lián)系在一起。
一個(gè) Redux 應(yīng)用中只有一個(gè) store璧针,store 保存了唯一數(shù)據(jù)源嚷炉。
store 的職責(zé)有:

  • 持有應(yīng)用的 state;
  • 提供 getState() 方法獲取 state探橱;
  • 提供 dispatch(action) 方法更新 state申屹;
  • 通過(guò) subscribe(listener) 注冊(cè)監(jiān)聽(tīng)器;
  • 通過(guò) subscribe(listener) 返回的函數(shù)注銷(xiāo)監(jiān)聽(tīng)器。
    let unsubscribe = store.subscribe(() =>
      console.log(store.getState())
    );
    
    unsubscribe();
    

redux 數(shù)據(jù)流

redux 架構(gòu)使用嚴(yán)格的單向數(shù)據(jù)流動(dòng)方式隧膏,其生命周期分為以下四步:

  1. 應(yīng)用調(diào)用 store.dispatch(action) 發(fā)送 Action
  2. redux 根據(jù)傳入的 action 調(diào)用對(duì)應(yīng)的 reducer 方法
  3. 根 reducer 把子 reducer 的結(jié)果合并成一顆 state 樹(shù)
  4. redux store 保存根 reducer 生成的 state 樹(shù)

得到的 state 樹(shù)即為當(dāng)前應(yīng)用的下一個(gè) state哗讥,所有訂閱 store.subscribe(listener) 的監(jiān)聽(tīng)器都將被調(diào)用。監(jiān)聽(tīng)器可以調(diào)用 store.getState() 獲得當(dāng)前 state胞枕。

React-Redux 組件

  • 如果一個(gè)組件既有 UI 又有業(yè)務(wù)邏輯杆煞,那怎么辦?回答是,將它拆分成:外面是一個(gè)容器組件决乎,里面包了一個(gè) UI 組件队询。前者負(fù)責(zé)與外部的通信,將數(shù)據(jù)傳給后者构诚,由后者渲染出視圖蚌斩。
  • React-Redux 規(guī)定,所有的 UI 組件都由用戶(hù)提供范嘱,容器組件則是由 React-Redux 自動(dòng)生成送膳。也就是說(shuō),用戶(hù)負(fù)責(zé)視覺(jué)層丑蛤,狀態(tài)管理則是全部交給它叠聋。

UI 組件(presentational component)

UI 組件負(fù)責(zé) UI 的呈現(xiàn)

  • 只負(fù)責(zé) UI 的呈現(xiàn),不帶有任何業(yè)務(wù)邏輯
  • 沒(méi)有狀態(tài)(即不使用 this.state 這個(gè)變量)
  • 所有數(shù)據(jù)都由參數(shù)(this.props)提供
  • 不使用任何 Redux 的 API

容器組件(container component)

負(fù)責(zé)管理數(shù)據(jù)和邏輯

  • 負(fù)責(zé)管理數(shù)據(jù)和業(yè)務(wù)邏輯受裹,不負(fù)責(zé) UI 的呈現(xiàn)
  • 帶有內(nèi)部狀態(tài)
  • 使用 Redux 的 API

react-redux

react-redux 提供了一個(gè) connect 函數(shù)碌补,用于把 react 組件和 redux 的 store 連接起來(lái),生成一個(gè)容器組件名斟,負(fù)責(zé)數(shù)據(jù)管理和業(yè)務(wù)邏輯,其簽名如下:

connect(mapStateToProps, mapDispatchToProps)(componentName)

mapStateToProps

輸入邏輯:外部的數(shù)據(jù)(即state對(duì)象)如何轉(zhuǎn)換為 UI 組件的參數(shù)魄眉。

  • mapStateToProps 是一個(gè)函數(shù)砰盐。它的作用就是像它的名字那樣,建立一個(gè)從(外部的)state 對(duì)象到(UI 組件的)props 對(duì)象的映射關(guān)系坑律。
  • 作為函數(shù)岩梳,mapStateToProps 執(zhí)行后應(yīng)該返回一個(gè)對(duì)象,里面的每一個(gè)鍵值對(duì)就是一個(gè)映射晃择。
  • mapStateToProps 會(huì)訂閱 store冀值,每當(dāng) state 更新的時(shí)候,就會(huì)自動(dòng)執(zhí)行宫屠,重新計(jì)算 UI 組件的參數(shù)列疗,從而觸發(fā) UI 組件的重新渲染。
  • mapStateToProps 的第一個(gè)參數(shù)總是 state 對(duì)象浪蹂,還可以使用第二個(gè)參數(shù)(假如參數(shù)名為:ownProps)抵栈,代表容器組件的 props 對(duì)象。
    • 使用 ownProps 作為參數(shù)后坤次,如果容器組件的參數(shù)發(fā)生變化古劲,也會(huì)引發(fā) UI 組件重新渲染。
  • connect 方法可以省略 mapStateToProps 參數(shù)缰猴,那樣的話产艾,UI 組件就不會(huì)訂閱 store,就是說(shuō) store 的更新不會(huì)引起 UI 組件的更新。

mapDispatchToProps

輸出邏輯:用戶(hù)發(fā)出的動(dòng)作如何變?yōu)?Action 對(duì)象闷堡,繼而從 UI 組件傳出去隘膘。

  • mapDispatchToProps 是 connect 函數(shù)的第二個(gè)參數(shù),用來(lái)建立 UI 組件的參數(shù)到 store.dispatch 方法的映射缚窿。
    • 換一種說(shuō)法棘幸,它負(fù)責(zé)把需要用到的 action 映射到展示組件的 props 上。
  • 也就是說(shuō)倦零,它定義了哪些用戶(hù)的操作應(yīng)該當(dāng)作 Action误续,傳給 store。
  • mapDispatchToProps 可以是一個(gè)函數(shù)扫茅,也可以是一個(gè)對(duì)象蹋嵌。
  • 如果 mapDispatchToProps 是一個(gè)函數(shù),會(huì)得到 dispatch 和 ownProps(容器組件的 props 對(duì)象)兩個(gè)參數(shù)葫隙。
    • mapDispatchToProps 作為函數(shù)栽烂,應(yīng)該返回一個(gè)對(duì)象,該對(duì)象的每個(gè)鍵值對(duì)都是一個(gè)映射恋脚,定義了 UI 組件的參數(shù)怎樣發(fā)出 Action腺办。
const mapDispatchToProps = (
  dispatch,
  ownProps
) => {
  return {
    onClick: () => {
      dispatch({
        type: 'SET_VISIBILITY_FILTER',
        filter: ownProps.filter
      });
    }
  };
}
  • 如果 mapDispatchToProps 是一個(gè)對(duì)象,它的每個(gè)鍵名也是對(duì)應(yīng) UI 組件的同名參數(shù)糟描,鍵值應(yīng)該是一個(gè)函數(shù)怀喉,會(huì)被當(dāng)作 Action creator,返回的 Action 會(huì)由 Redux 自動(dòng)發(fā)出船响。
  • 舉例來(lái)說(shuō)躬拢,上面的 mapDispatchToProps 寫(xiě)成對(duì)象就是下面這樣。
const mapDispatchToProps = {
  onClick: (filter) => {
    type: 'SET_VISIBILITY_FILTER',
    filter: filter
  };
}

參考

  1. 有關(guān) mapStateToProps 與 mapDispatchToProps见间,還可以參考《深入淺出React和Redux》P71-73聊闯,【3.2.5 React-Redux】章節(jié)。
  2. 阮一峰的網(wǎng)絡(luò)日志 -> Redux 入門(mén)教程(三):React-Redux 的用法

    有對(duì) connect(mapStateToProps, mapDispatchToProps)(componentName) 的詳細(xì)解讀米诉,上面有關(guān) mapStateToProps 與 mapDispatchToProps 的解讀文字菱蔬,均來(lái)自這篇文章(略有修改)。

  3. Redux 官方文檔中文翻譯
  4. Redux 文檔 -> 英文原版
  5. 前端手記 TodoMVC 之 Redux 篇

擴(kuò)展閱讀

  1. 劉一奇 -> React 與 Redux 系列教程史侣,一共八篇文章
  2. 理解Redux應(yīng)用架構(gòu)——(一)Redux結(jié)構(gòu)概覽
  3. 理解Redux應(yīng)用架構(gòu)——(二)創(chuàng)建store的createStore
  4. 理解Redux應(yīng)用架構(gòu)——(三)讓你愛(ài)上的Redux middleware

個(gè)人學(xué)習(xí)心得項(xiàng)目地址

關(guān)于作者

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末汗销,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子抵窒,更是在濱河造成了極大的恐慌弛针,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件李皇,死亡現(xiàn)場(chǎng)離奇詭異削茁,居然都是意外死亡宙枷,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)茧跋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)慰丛,“玉大人,你說(shuō)我怎么就攤上這事瘾杭∽绮。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵粥烁,是天一觀的道長(zhǎng)贤笆。 經(jīng)常有香客問(wèn)我,道長(zhǎng)讨阻,這世上最難降的妖魔是什么芥永? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮钝吮,結(jié)果婚禮上埋涧,老公的妹妹穿的比我還像新娘。我一直安慰自己奇瘦,他們只是感情好棘催,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著耳标,像睡著了一般醇坝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上麻捻,一...
    開(kāi)封第一講書(shū)人閱讀 51,598評(píng)論 1 305
  • 那天纲仍,我揣著相機(jī)與錄音呀袱,去河邊找鬼贸毕。 笑死,一個(gè)胖子當(dāng)著我的面吹牛夜赵,可吹牛的內(nèi)容都是我干的明棍。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼寇僧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼摊腋!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起嘁傀,我...
    開(kāi)封第一講書(shū)人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤兴蒸,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后细办,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體橙凳,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了岛啸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钓觉。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖坚踩,靈堂內(nèi)的尸體忽然破棺而出荡灾,到底是詐尸還是另有隱情,我是刑警寧澤瞬铸,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布批幌,位于F島的核電站,受9級(jí)特大地震影響赴捞,放射性物質(zhì)發(fā)生泄漏逼裆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一赦政、第九天 我趴在偏房一處隱蔽的房頂上張望胜宇。 院中可真熱鬧,春花似錦恢着、人聲如沸桐愉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)从诲。三九已至,卻和暖如春靡羡,著一層夾襖步出監(jiān)牢的瞬間系洛,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工略步, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留描扯,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓趟薄,卻偏偏與公主長(zhǎng)得像绽诚,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子杭煎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355