Redux有哪些最佳實踐?

正在學習目前在flux里最熱門的redux,其中基本的概念和用法都懂了,求教大神們在用redux時候的各種有用的經驗和實踐


分享一些在我們自己項目中總結的實踐,並不一定對所有人都是最佳實踐。


首先Redux是有自己的三大理念的(Three Principles),所以我們的最佳實踐都是基於這三個理念的(有例外情況,後面會介紹):

  • Single source of truth:應用程序的所有 state 應該被保存在單個 store 中。
  • State is read-only: state 不能被直接修改,只能通過觸發 action 來修改。
  • Changes are made with pure functions: 使用純函數式的 reducer 來處理數據修改邏輯。純函數意味著沒有side effect,也就是處理邏輯不能依賴於全局變數,只能依賴於傳入的參數;不能修改傳入的參數,返回的應該是一個全新的對象。


下面是一些基於這三個理念衍生出的實踐:

關於 Store

1. API 返回的數據與 view 需要的數據分開放在不同的節點上。API 的數據其實是復用程度更高的數據,與 view 的數據隔離開,一方面可以幫助我們反省 view 中是否有冗餘的數據,另一方面可以更容易的讀取 API 返回的數據。


2. 存儲 API 返回的數據時,應該與最原始的數據保持一致。這樣才不會限制我們未來做轉換的可能性。


3. View 中只存儲不可以被計算出的數據。存儲 View 數據時,一定要時刻反省這個到底是可以從其他數據中計算出來的,還是需要緩存的。對於可以從其他數據中計算出來的,就不要重複存儲一份了,直接在 selector 中計算出來就可以了。因為冗餘數據的同步維護,是非常費力並且容易出錯的。


4. 臨時數據不一定要進 store。例如查詢結果這種數據,可以直接放在 react view 的 state 中。但如果查詢結果需要被緩存,切換頁面回來後還能看到,那還是要進 store。

關於 Action

1. 使用 flux standard action(GitHub - acdlite/flux-standard-action: A human-friendly standard for Flux action objects.)標準化的 action 數據結構可以讓維護更容易,也讓第三方庫的引入更容易。

2. Action 只做簡單的參數傳遞和配置,最多做一些數據格式的轉換,例如:把 date 轉成 formatted string。盡量把數據處理的邏輯放在reducer中。因為按照語意,action 就只是一個 identifier,reducer 才是負責邏輯處理的。


3. 對於非同步請求的 action,action 不要真的 call ajax,只 create ajax options,call ajax 放在 middleware 中。因為 ajax action 往往會觸發後續的一些 error action,只有在 middleware 中才能控制 action flow,觸發這些後續的 error action。

關於 Reducer

1. Reducer 中只處理和保存需要被緩存,也就是無法被 selector 直接計算出的數據,其他的數據邏輯都放在 selector 中計算出來。這個其實和 store 的第三條類似,主要是為了防止冗餘數據的出現,所以能用計算得出的數據就不要存儲了。


2. 使用 normalizr(GitHub - paularmstrong/normalizr: Normalizes nested JSON according to a schema)處理API數據。這個也是為了防止冗餘數據的出現。

3. 針對 API,不同的 domain 應該有不同的 reducer。針對 view,不同的模塊應該有不同的 reducer,最後通過 combineReducers 來 combine 到一起。這樣做主要是為了有更清晰的模塊化。

關於 Selector

我們傾向於把所有的 view 的數據邏輯都放在 selector 中,而不是分散到每個 view 的 render 方法中。這樣的好處是你很容易在多個 selector 中發現重複的邏輯,抽出共用的方法。但是也有壞處,就是 selector 往往會非常大(我們的 selector function 會單獨放一個文件),container 的 props 會非常多,並且 container 需要知道所有子 view 的顯示邏輯。不過為了增加代碼的復用性,減少重複代碼,這些都是值得的。


不是大神,但有經驗。

1. 用ES6, webpack, react-hot-loader....詳細內容參照MERN v2.0 - Build production ready universal apps easily

2. 根據你的需求,選擇middleware (thunk vs saga?...)

3. https://github.com/gaearon/redux-devtools

4. 關於文件的結構,小項目用類型分類,大項目用feature分類

5. 區分smart component (know the state) 和 dump component (stateless)

6. component里不要出現任何async calls,交給action creator來做

7. reducer盡量簡單,複雜的交給action creator

8. reducer里return新state的時候:

// add new item to state array

// bad and does not work
case "ADD":
return state.push(newItem);

// Good
case "ADD":
return [
...state,
newItem
];

// delete new item to state array

// bad and does not work
case "DELETE":
return state.splice(index, 1);

// Good
case "DELETE":
return state.slice(0, index).concat(state.slice(index + 1));

// update new item to state array

// First way
case "EDIT":
return state.slice(0, index)
.concat([{id: "id", value: "newValue"}])
.slice(index + 1);
// Second way
case "EDIT":
return state.map((item) =&> {
if (item.id === "id") {
return {
...item,
value: "newValue"
}
} else {
return item;
}
})

9. action creator里,用promise/async/await以及redux-thunk來幫助你完成想要的功能

10. 在test里不管你用tape還是mocha,請用http://airbnb.io/enzyme/

11. 有些時候有些項目你並不需要redux


1. action creators和reducer請用pure函數,不要副作用。
2. immutable.js配合效果很好。
3. 項目大了請用ducks module https://github.com/erikras/ducks-modular-redux
4. reducer里只存儲必要的state, 需要推導的都放到selector里(connect函數的第一個參數)
5. reducer和selector請放到一個文件,請記住selector之間可以compose.
6. 請善用higher order component. 如果某一項功能是多個組件通用,higher order component往往要比套一層container更靈活。
7. 請慎重選擇組件樹的哪一層使用connected component(連接到store),通常是比較高層的組件用來和store溝通,最低層組件使用這防止太長的prop chain.
8. 項目大了之後請用redux-saga或者redux-observables
9. 請慎用自定義的redux-middleware,錯誤的配置可能會影響到其他middleware.

暫時這麼多,想到再補充。


1. 當創建action的時候,如果按照官方的做法會寫很多,你可以試試redux-action之類的庫,其實就是語法糖。

2. 可以找一個middleware來做async的操作,redux promise之類的,然後也可以再封裝一層middleware,fetch_start之類的操作可以不用寫了,然後在後面直接監聽completed操作。當然,靈活性會差一些,大多數時候還是比較有用。也可以直接上redux-saga處理複雜的控制流。

3. redux的還有一個優勢是做統計方面的需求很簡單,直接加個middleware,然後發統計信息就行了,大概就和redux-logger這樣的差不多,簡單輕鬆。

4. 結合react的一些特性使用,willReceiveProps裡面判斷是不是re-rendering,會減少一些需要一些順序執行actions的情況。

5. connect的時候根據組件來connect,這樣會好維護一點,別從頂層直接什麼都扔一個component裡面。

6. form使用 redux-form, 這樣可以抽離form組件,其實和5是一個意思,就是是有個開源庫做了form表單組件。

7. redux裡面貌似是少有局部的,大多數時候少用setState還是對的,但是也不一定。總體來說局部狀態是用state還是處理到state tree上,只能說看情況,跟外部的關係相關。如果和外部某些關係比較強,做在state tree上,如果是外部不可見,最好state。


先上代碼 https://github.com/hyy1115/react-redux-webpack

分享我從自己架構過的多個reudx項目中總結出來的東西,不只是一個demo,不只是一個todo List,而是一個完整的react-redux-webpack開發方案,喜歡就拿去用。前後端交互模式,redux維護模式,webpack hot模式,react組件開發模式,開發過程中需要的功能都配置了案例。

不敢說是redux的最佳實踐,但算是我自己目前的最佳實踐。


仔細閱讀 Redux 官方里的一個 issue,集思廣益,不過就是略長。

Recommendations for best practices regarding action-creators, reducers, and selectors · Issue #1171 · reactjs/redux · GitHub


竊以為,最佳實踐是在深刻理解 Redux 的前提而來。此時您需要 Redux 莞式教程,讓您使用的時候可以胸有成竹 GitHub - kenberkeley/redux-simple-tutorial: Redux 莞式教程。本教程深入淺出,配套入門、進階源碼解讀以及文檔注釋豐滿的 Demo 等一條龍服務


我來安利一波我們的最佳實踐。

redux會將所有組件的state全部都扔進一個大的state中去維護,每次reducer只會更新一小部分。這要求開發者要自行構建良好的state組織結構,否則當頁面組件越來越多時,很容易被一個超級大的state繞暈。

我們現在在項目中使用了redux-arena,它可以將redux與react打包成一個模塊載入,如果react組件被卸載,那麼react組件在redux中的state/reducer/saga都會被自動卸載,徹底解決state樹和reducer過於龐大的問題。

一張gif圖說明redux-arena做了什麼,如下圖所示,當切換pageA和asyncPageB的掛載和卸載,會觸發redux中全局state樹的改變,所有與當前頁面展示無關的state和reducer徹底不復存在,你看到的state一定是當前被掛載的組件的state,極大地提升了state的可讀性。

項目的github地址:hapood/redux-arena

還有一篇相關介紹在這裡:https://zhuanlan.zhihu.com/p/28690716


這個還是看下作者怎麼說

--&> https://egghead.io/courses/building-react-applications-with-idiomatic-redux &<-- 看這裡

== 惡搞圖片==


這裡不多說了

還是這個個人覺得最牛的腳手架

GitHub - bodyno/react-starter-kit: 完美使用 React, Redux, and React-Router!最好用的腳手架

看源碼的時候要仔細

收穫肯定是很大的


有幾個Github上一些比較完整的Redux項目可以作為參考:

todo-redux-react-webpack Redux作者在egghead上的教程sample code,很官方....

menu-plus : 意外發現的一個純SPA,項目架構較完整,React+Redux+React Router

sound-redux: 一個音樂網站,完成度很高,但缺點是每個組件都攜有整個redux state

food-lookup-demo : Fullstack的食物搜索app 可以參考他的api處理方法和unit test


我認為只從頂層傳入數據的做法太複雜了。

如果子孫很多,那麼頂層要接入十幾二十個數據,然後一層層分發下去,思考負擔很重,對可讀性,可維護性,都是一種損害。

應該讓組件和數據是一一對應的關係,每個組件自己接入想要的數據。這會導致多寫一些重複邏輯,沒關係,抽出公共方法就行了。總比一層層傳props方便。

這樣也導致介入點很多,介入點很多會造成混亂嗎?我認為不會,只要數據和組件是一一對應的,就不會混亂。混亂來於數據歸屬不清晰。


配immutable 挺好的。


最近在使用redux-observable,具體這種流式編程配合redux非常的完美,最佳實踐可以看一下 redux-obervable的官方示例 redux-observable/redux-observable


看成了「durex有哪些最佳實踐?」怎麼破


自己做的項目用過redux。最佳實踐是除非你只做1版,要不就不要用。

嘗試過後的結論,寧用flux也別用redux。迭代的太快了,version 各種變。更不要提裡面一堆概念了,想法是好的,敢不敢穩定點。


推薦閱讀:

Weex 和 React Native 的根本區別在哪裡?
前端自學,目前可以用react寫一些項目,但是不知道目前現在在前端上的水平,希望可以獲得指點?
初級前端er如何學習react.js?
Web Component 和類 React、Angular、Vue 組件化技術誰會成為未來?
現在(2017年)是否還有必要學習jQuery?

TAG:Web開發 | 前端開發 | JavaScript | 編程 | React |