聽說你需要這樣了解 Redux
這是一篇寫在兩年前的文章了,部門內容可能有點過時,但大體邏輯差不了太多,正文分為三個小章節。點擊這裡閱讀原文 體驗更加。
聽說你需要這樣了解 Redux(一)
1. 寫在前面
對於複雜的 Web 交互應用,相繼出現了一系列的 MV* 框架,09 年的 Angular 帶動了一系列的發展。
在後期的開發過程中,大家發現 Angular 太過於重,雙向綁定的數據流容易讓數據變得不可控制。
在任何應用中管理數據都是必不可少的。通過用戶界面來編排數據流是一項具有挑戰的工程。現代的 Web 應用會涉及複雜的 UI 交互,比如改變了一個地方的 UI 數據,需要直接或者間接的改變其他地方的 UI 數據。一些雙向綁定的框架(比如:Angular.js 等)都是對這種場景比較好的解決方案。
對於一些應用(尤其是數據流比較簡單的),這種雙向綁定是非常快速和足夠的解決方案。但是對於一些更加複雜的應用場景,數據雙向綁定已經被證明是不夠的,它會妨礙用戶界面的設計。實際上,React 並沒有解決這樣一個複雜應用中比較複雜的數據流問題(雖然後面出現了 Flux 這種解決方案),但是他確實解決了一個組件中數據流的問題—— React 數據陣營之 State 與 Props
React 的橫空出世一度讓 Web App 的概念如火如荼,這種單向的數據流也就需要新的模式來組織。
在這個過程中對狀態的管理相繼出現了 Flux 等解決方案,在後來的發展中,Redux 由於單一的 Store 讓大家更是喜歡。
2. Just Do
2.1 拋開全家桶
React 開發和全家桶離不開關係,但全家桶極有可能阻礙我們對 Redux 的理解。
這裡我們拋開全家桶,用 jsfiddle 來學習 Redux。
2.2 幾個概念與一個狀態機
1. store:
Redux 是用來管理 App 中狀態的。 和 Flux 相比,Redux 最鮮明的特點就是只有一個 Store。那這個 Store 到底是用來幹什麼的?
很直觀的, Store 就是用來儲存整個應用的狀態的,也就是 React 中的 state。
Store 除了儲存整個應用的狀態之外,還提供幾個介面,包括: 組件獲取狀態的介面 getState()、組件通過 action 和數據進行 dispath 更新 Store 的介面 dispatch(action)、Store 發生改變之後進行 subscribe 改變組件 view 的介面 subscribe()。
2. action:
action,翻譯過來就是 動作 的意思。他用來接受一個指示和數據,然後由 action 進行 dispatch 來根據這個動作和數據進行 Store 的更新。
常見的action格式為如下,包括一個 type 和 text(可以為空):
{ type: Action1, text: { content1: content1_val, content2: content2_val }}
3. reducer:
Store 中數據的更新通過 action 進行 dispatch,但 action 指示提供了 action_type 和 action_text,並沒有去決定 Store 應該如何更新。
reducer 可以理解為 action 和 Store 進行關聯的時候中間的一層,他接收 action_type 和 action_text,然後 switch action_type,在相關的條件下對相關的數據進行整合,最後結合原 Store 的 state 與 action 裡面的 type/text 生成新的 state 給 Store,進行 Store 的更新。
4. Redux 工作狀態機:

2.3 使用梗概
Redux is a predictable state container for JavaScript apps
Redux 只有一個單一的數據源,並且這個數據源是只讀的,修改這個數據源必須通過 dispatch action,action 只是告知了 type 和 text,這個時候 reducer 結合上一個 state 和 action 做出自己的響應,返回新的 state 送入 store。
在 reducer 中,他接收上一個 state 與 action,返回一個新的 state。這個過程是純函數式的,返回的結果由進去的參數決定,不會隨著外界的變化而發生改變。
2.4 入門小例子
jsfiddle 見: https://jsfiddle.net/rccoder/shmxthbo/2/
const { createStore } = Reduxconst reducer = (state, action) => { let result = state switch (action.type) { case "TO_NUM": result.value = action.text break; case "TO_WORD": result.value = action.text break; } return result}const store = createStore(reducer, {value: })store.subscribe(() => { console.log(store.getState())})store.dispatch({type: TO_NUM, text: 100})store.dispatch({type: TO_WORD, text: "ABC"})store.dispatch({type: TO_FLOAT, text: 100.1})
上述的例子最後會出現結果:
Object {value: 100}Object {value: "ABC"}Object {value: "ABC"}
如我們所願,subscribe 監聽 store 的變化得到上面的結果。
其中,createStore 接收的兩個參數分別是 reducer 和 initState,為了讓代碼更加優雅,我們還可以把 initState 抽離出來:
const { createStore } = Reduxconst initState = { value: }const reducer = (state, action) => { let result = state switch (action.type) { case "TO_NUM": result.value = action.text break; case "TO_WORD": result.value = action.text break; } return result}const store = createStore(reducer, initState)store.subscribe(() => { console.log(store.getState())})store.dispatch({type: TO_NUM, text: 100})store.dispatch({type: TO_WORD, text: "ABC"})store.dispatch({type: TO_FLOAT, text: 100.1})
再其次,在項目中為了項目的規範化,我們需要對所有的 action_type 常量進行統一的管理,放到單獨的文件中,這樣可以進一步的抽離:
const { createStore } = Reduxconst initState = { value: }const TO_NUM = TO_NUMconst TO_WORD = TO_WORDconst TO_FLOAT = TO_FLOATconst reducer = (state, action) => { let result = state switch (action.type) { case "TO_NUM": result.value = action.text break; case "TO_WORD": result.value = action.text break; } return result}const store = createStore(reducer, initState)store.subscribe(() => { console.log(store.getState())})store.dispatch({type: TO_NUM, text: 100})store.dispatch({type: TO_WORD, text: "ABC"})store.dispatch({type: TO_FLOAT, text: 100.1})
2.5 小進階
2.5.1 middleware
Redux 解決的正是狀態的問題。在平時開發過程中,我們可能需要知道上一個狀態以及現在的狀態,以及發生這個改變的 action 與數據是什麼。
這個時候就需要 middlerware 出廠了,middlerware 發生在 action 之後,reducer 之前,在 middlerware 中,我們調用 next(action) 函數就可以把 action 傳遞給 reducer。
middlerware 不僅僅能實現查看當前 action 與 action 旁邊的兩個狀態,還能對特定的 action 最一些特定的處理,然後再傳給 reducer。這也正是 middlerware 的本意。
我們在上面的代碼上加上 middlerware,列印一下這個小程序的日誌:
jsfiddle 見:https://jsfiddle.net/rccoder/shmxthbo/3/
const { createStore, applyMiddleware } = Reduxconst reducer = (state, action) => { let result = state switch (action.type) { case "TO_NUM": result.value = action.text break; case "TO_WORD": result.value = action.text break; } return result}const log = store => next => action => { console.log(PRE_STATE:) console.log(store.getState()) console.log(`ACTION_TYPE: ${action.type}`) console.log(ACTION_TEXT:) console.log(action.text) let result = next(action) console.log(NEXT_STATE:) console.log(store.getState()) console.log(----) return result}const store = createStore(reducer, {value: }, applyMiddleware(log))store.subscribe(() => { //console.log(store.getState())})store.dispatch({type: TO_NUM, text: 100})store.dispatch({type: TO_WORD, text: "ABC"})store.dispatch({type: TO_FLOAT, text: 100.1})
結果如下:
可以說這個日誌比較明顯的反應了程序中狀態的轉移與轉移條件。
加上 middlerware 之後整個 Redux 的運行狀態轉移圖如下:

2.5.2 拆分 reducer
上面例子中 Store 的 State 是很簡單的:
{ value: }
在真是環境中,state 往往是複雜的。對於 reducer 而言,我們也可能根據相應的業務需求拆分出不同的 reducer,這樣就避免了在一個文件裡面寫出一堆的 case。
對於下面的 Store
{ businessA:{ m: 1, n: 2 }, businessB: [ 1, 2 ]
我們可以拆分為兩個 reducer
const reducerA = (state = {m: 1, n: 2}, action) => { switch(action.type) { case "A_TYPE1": return {...state, m: 2} break; case "A_TYPE2": return {...state, k: 3} break; default: return state break; }}const reducerB = (state = [1, 2], action) => { switch(action.type) { case "B_TYPE1": return [].concat(state.slice(0)) break; default: return state break; }}// 合併 reducerconst reducer = (state = {}, action) => { return { businessA: reducerA(state.businessA, action), businessB: reducerB(state.businessB, action) }}
如此, reducer 不再負責整個 reducer,而是把它下分到 reducerA 和 reducerB 兩個 reducer, 每個 reducer 互相獨立。
這種做法是非常值得推薦的,尤其是在大型應用中, Redux 官方也給出了合併 reducer 的 API:
const {combineReducers} = reduxconst reducer = combineReducers({ businessA: reducerA, businessB: reducerB})
結合上面的描述,對貫穿整個的例子做一個修改:
jsfiddle 見 https://jsfiddle.net/rccoder/shmxthbo/5/
const { createStore, applyMiddleware, combineReducers } = Reduxconst TO_NUM = TO_NUMconst TO_WORD = TO_WORDconst PLUS = PLUSconst NUM_WORD_reducer = (state = , action) => { switch(action.type) { case TO_NUM: return action.text break case TO_WORD: return action.text break default: return ACTION_NUM_WORD_NO_TEXT break }}const NUM_PLUS_reducer = (state = 0, action) => { switch(action.type) { case PLUS: return state+action.text break default: return state break }}const reducer = combineReducers({ NUM_WORD: NUM_WORD_reducer, NUM_PLUS: NUM_PLUS_reducer})const log = store => next => action => { console.log(PRE_STATE:) console.log(store.getState()) console.log(`ACTION_TYPE: ${action.type}`) console.log(ACTION_TEXT:) console.log(action.text) let result = next(action) console.log(NEXT_STATE:) console.log(store.getState()) console.log(----) return result}const store = createStore(reducer, {}, applyMiddleware(log))store.subscribe(() => { //console.log(store.getState())})store.dispatch({type: TO_NUM, text: 100})store.dispatch({type: TO_WORD, text: "ABC"})store.dispatch({type: TO_FLOAT, text: 100.1})
2.5.3 Redux 非同步
我們需要保持 reducer 是函數式的,這也就意味著在 reducer 中相同的輸入一定有相同的返回,並且這個返回時及時的。
在應用開發中,我們經常會遇到一些非同步的請求,這個需要顯然是存在的一個問題。
通常的,我們有下面的幾個解決方案:
2.5.3.1 理所當然
對於非同步這種情況理所當然我們有著這樣一種想法:在非同步得到結果之後進行 dispatch。
setTimeout(() => { dispatch(action)}, 2000)
在簡單場景下,這種做法是完全接受的。但是當非同步比較多比較複雜的時候,他的缺點也就暴露了出來:
- 無法復用非同步代碼,尤其是用 Promise 等的時候會出現一堆的同質代碼(理論上我們可以抽象,見後面的 ActionCreater)
- 多個非同步不好統一處理
2.5.3.2 middleware 上做文章
前面在介紹 middleware 的時候,提到 middleware 是介於 Action 與 Reducer 之間的,所以當 action 經過 middleware 的時候,完全由 middleware 控制。
const m = store => next => action => { setTimeout(() => { ... // 針對老的 action 進行處理,生成新的 action next(newAction) }, 2000)}
在 middleware 上做文章顯然比理所當然那種方法好很多,我們可以封裝相應的 middleware,然後做出不同的處理,把非同步封裝在 middleware 中。
無疑,在 middleware 上做文章是優雅的。
2.5.3.3 第三方組件
redux 本身不提供非同步,不代表第三方也不支持,有下面的一些 thunk 能方便我們在 redux 中進行非同步操作。
2.5.3.3.1 redux-thunk
redux-thunk 是 redux 官方欽定的非同步組件,實質是 redux 的一個 middleware。
正如官方的介紹,redux-thunk 的目的就是讓同步操作與非同步操作以同一種方式來 dispatch:
- dispatch(action)
- dispatch(thunk)
如上,同樣的 API 去實現非同步操作。
thunk 是什麼?
thunk 這是一個神秘的單詞,在不同的場合其意義有所差別。在這裡,如官方所言:「A thunk is a function that wraps an expression to delay its evaluation.」他就是一個延時的函數,其意義與函數式編程中的惰性有點相同的意思編譯器的"傳名調用"實現,往往是將參數放到一個臨時函數之中,再將這個臨時函數傳入函數體。這個臨時函數就叫做 Thunk 函數。(https://i5ting.github.io/asynchronous-flow-control/#702)
store.dispatch(dispatch => { fetch(...) .then(data){ dispatch({action: xxx, text: JSON.parse(data)}) }})
如上,redux-thunk 判斷 store.dispatch 傳遞進來的參數,如果是一個 thunk(延時函數),就處理 thunk 裡面的東西,完事之後執行這個函數。
縱觀 redux-thunk 的源碼,也能感嘆設計之美:
function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === function) { return action(dispatch, getState, extraArgument); } return next(action); };}const thunk = createThunkMiddleware();thunk.withExtraArgument = createThunkMiddleware;export default thunk;
在實際操作中,我們可能需要標記非同步是否實現完畢,一般情況下我們可以設置兩個 Action 來標記:
store.dispatch(dispatch => { dispatch({action: FETCH_START, text: }) fetch(...) .then(data){ dispatch({action: FETCH_END, text: JSON.parse(data)}) }})
2.5.3.3.2 其他
比較著名的 redux 非同步組件還有 redux-premise、redux-saga 等,大家可以自行研究(我還沒使用過??)
2.5.4 Action Creater
尤其是在有非同步操作的時候,比如我們 dispatch(thunk) 的時候,這個 thunk 是比叫大的,理論上我們需要對他封裝一下:
// 原store.dispatch(dispatch => { dispatch({action: FETCH_START, text: }) fetch(...) .then(data){ dispatch({action: FETCH_END, text: JSON.parse(data)}) }})// 封裝之後store.dispatch(fetch_thunk(text))const fetch_thunk = (text) => { return dispatch => { dispatch({action: FETCH_START, text: }) fetch(...) .then(data){ dispatch({action: FETCH_END, text: JSON.parse(data)}) } }}
我們發現,又可以優雅的 dispatch 了。 ?
2.5.5 immutable
我們知道 Redux 的風格是函數式的,在函數式編程中經常會出現一個重要的概念: immutable。
在 Redux 中,Store 中的 state 應該就是 immutable 的,在上面的例子中,我們明顯的看到每次不是修改 state, 而是返回一個新的 state。這個過程中,我們就希望 state 是 immutable 的。
在 JavaScript 中,沒有這個特定。
immutable 要求的是值本身不被修改,這點也是和 const 的區別(「指針」不發生變化,比如用 const 聲明一個 Object 之後,我們還能在給這個 Object 添加新的 key-value 對);最需要注意的是在 JavaScript 中引用類型相關(Object、Array)的變數。例如:
let a = 1let b = ab = 2console.log(a)console.log(b)let c = {m: 1}let d = cd.n = 2console.log(c)console.log(d)
在上面的例子中,我們會發現 a 不會隨著 b 的改變而發生變化,而 c 會隨著 d 的改變發生變化。顯然這個是不符合 immutable 的!
對於這個問題,我們可以使用一些沒有副作用的方法來解決,比如對 Array 使用 concat 生成新的數組、對 Object 使用解構等
let a = {m:1}let b = {...a, n:2}console.log(a)console.log(b)let c = [1, 2]let d = [].concat(c)c.push(3)console.log(c)console.log(d)
在實際開發過程中,我們可能會覺得像上面一樣做可能容易犯錯誤,更重要的一點是直觀地,我們會感覺到如果數據是 immutable 的,性能應該會更好,當然事實上也是如此。針對這個問題,我們可以使用一些庫然後利用他的 API 來操作數據去保證數據是 immutable 的。
比較著名的一些 immutable 庫有:
- immutable-js
- seamless-immutable
2.6 與 React 合作 —— react-redux
redux 是一個處理狀態的庫,儘管你聽他的時候他經常和 react 出現,但這並不意味著 redux 只能和 react 一起用。他還可以和 Angular、backbone 等使用,甚至可以直接使用 redux,就和上面的描述一樣。
嚴格來說他們是互相分離的,中間使用官方的 react-redux 就可以完美的把 redux 與 react 結合起來。
2.6.1 provider
Provider 是在原有的 APP 上麵包一層,然後接收 store 作為 props,然後給 connect 用
2.6.2 connect
connect 應該是 react-redux 的核心。他接收 store 提供的 state 和 action,返回給我們的 react 組件;還有一個很重要的一點是在 connect 這層 react-redux 做了優化,保證給組件只傳和他相關的 state,這也就保證了性能上的優勢
聽說你需要這樣了解 Redux(二)
1. 前言
很久很久之前,寫過一篇介紹 Redux 相關概念的文章 聽說你需要這樣了解 Redux(一),隨後因為實習的事情拖延了後續(根本原因是懶)。
在實習中也更加深刻的認識了 Redux 相關的內容。除此之外,翻譯 Redux 中文文檔 redux-in-chinese 也讓我靜心下來仔細品讀了設計理念。
這篇文章中我將介紹一個如何把 React 和 Redux 這兩個大殺器結合起來,同時也會介紹一些 state、reducer 設計相關的東西。
2. 正文
2.1 連接神器 react-redux
react 和 redux 可以說是沒有任何關係,redux 主要提供一個全局的 store,然後 react 從 store 拿去數據,store 發生變化,react 重新進行渲染。
在遠古時代我們就已經知道頻繁操作 DOM 是很耗費性能的,react 在數據發生變化的時候就會進行 render,雖然我們可以用 PureRenderMixin 或者在 shouldComponentUpdate 做一些處理,但對於一個頁面的數據來說,這好像也是非常麻煩的。
react-redux 的出現就解決了這個問題,並且保證了整個應用程序的靈活性與可維護性。用官方的話說就是:
Performant and flexible
react-redux 提供了兩個耳熟能詳的 API: <Provider store> 和 connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])。
在上篇文章中已經簡單介紹過這兩個 API 的基礎概念:
Provider 是在原有的 APP 上麵包一層,然後接收 store 作為 props,然後給 connect 用
connect 接收 store 提供的 state 和 action,返回給我們的 react 組件;還有一個很重要的一點是在 connect 這層 react-redux 做了優化,保證給組件只傳和他相關的 state,這也就保證了性能上的優勢
2.2 搭個架子
//index.jsximport { ReactDOM } from react-dom;import { Provider } from react-redux;import App from ./App;ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.querySelector(#app));// App.jsximport { React } from react;import { connect } from react-redux;class App extends Component { render() { <div>Test<div> }}export default connect(state => state, actions)(App);
這裡 connect 只傳遞了兩個參數 state => state、actions。
state => state 就是 mapStateToProps 的函數,他會監聽 store 的變化,只要 store 變化,這個函數就會被調用。mapStateToProps 返回一個對象,這個對象會與組件的 props 合併。mapStateToProps 接收兩個參數 state、ownProps,通常情況下我們只需要第一個參數,如果有第二個參數 ownProps,其也將傳給組件的 props,並且當組件的 props 發生變化時也會調用 mapStateToProps。這個例子中進去與返回的都是 state,這在實際開發中是不科學的,為了保證我們應用的性能,我們應該只返回需要的 state 切片。
action 就是 mapDispatchToProps,他可以是個函數,也可以是個對象。如果是個函數,這個函數將接收一個 dispatch 函數,然後由我們決定返回怎樣的一個對象,這個對象會通過 dispatch 函數與 action creator 綁定起來;如果是個對象,則這個對象中的函數都會被當做 action creator,而且這個對象會與 store 綁定在一起,其中所定義的方法名將作為屬性名,合併到組件的 props 中。一般情況下會省略這個參數。
mergeProps 用於自定義 props 合併,默認返回 Object.assign({}, ownProps, stateProps, dispatchProps)。
options 是一個優化開關,這裡不做太多解釋(我還沒用過啊!)。
關於 connect 詳細的說明,還是建議閱讀官方文檔:https://github.com/reactjs/react-redux/blob/master/docs/api.md
這裡有個小聰明可以玩,如果你已經用上了 babel 全家桶並且是一個激進的人,可以用裝飾器優雅的寫 connect:
@connect(state => state, actions)export default class App extends Component { render() { <div>Test<div> }}
2.3 store 綁定
從用 React 的第一天起,「單向數據流」 的概念就已經扎進了心口。在上面的例子中,我們可以看到一個簡單清晰的流向:
從 store -> connect 層做處理 -> 組件 props
那在實際的交互中,又是如何的往 store 中塞入數據的呢?這個就和 Redux 連接起來了。
import {createStore, applyMiddleware, compose} from "redux";import thunkMiddleware from "redux-thunk";const rootReducer = (state, action) => { let result = state; switch(action.type) { case "TYPE_A": // 省略 break; default: break; } return result;}const initialState = {};const middleware = [thunkMiddleware];const store = createStore( rootReducer, initialState, compose( applyMiddleware(...middleware), window.devToolsExtension ? window.devToolsExtension() : f => f ))
和上章節中一樣,用 Redux 提供的 createStore 創建 Redux store,然後把 store 在 Redux 提供的 Provider 中傳遞進去。
同樣,通過 store.dispatch({type: xx, text: xx}) 就能融入整個 Redux 中。每當 store 發生變化,react-redux 就會處理 subscribe 與 getState,傳給相應的組件,同時也保持了比較高的性能。
2.4 state、reducer 設計
參考我翻譯的 redux 文檔:State 範式化、管理範式化數據
裡面有詳細的說明,並且有很不錯的例子。更應該的,你應該詳細閱讀一下所有文檔,尤其是 組織 Reducer這塊。
聽說你需要這樣了解 Redux(三)
Redux 非同步
隨著場景的複雜化,在非同步結果得到之後再進行 dispatch 變得越來越不可取與不可維護。Redux 自身並沒打算解決這個問題,但由於提供了 middleware,可以在這層上做文章。
比較熟知的 Redux 非同步 middleware 有 redux-thunk, redux-promise, redux-saga 等。
關於這些 middlware 如何使用,可以參考我近期校對過的文章 Redux 非同步四兄弟,如果懷疑文章翻譯的準確度,可以直接看原文 Redux 4 ways。
標準化的 action
Redux 自身沒有要求 action 應該是什麼格式,但按照一些實踐,我們知道 action 一般包含 type 和 payload。
那如何更好的去寫出標準化的 action 呢?可以看 redux-actions。
如果不想仔細看 redux-actions 相關介紹,可以簡單看一下我下面的介紹:
團隊開發中的標準化 action
如果在多人開發中,不對 action 的設計做一定的規範,就可能會出現多種不同形式的 action,比如:
type:...|text:...{ type: ..., data: ...,}{ actionType: ..., data: ...,}{ type: ..., payload: ...,}
那如何去設計一個大家可以都接受的 action 呢,在action 進化的歷程中,Flux Standard Action 的設計受到廣為推崇。
它約定一個 action 必須是一個對象,並且至少包含一個 key 為 type 的屬性表示 action 類型。除此之外,還可以有 payload 屬性表示 action 攜帶的數據,error 屬性表示 action 是否是 「錯誤的」,meta 屬性表示其他一些信息,當然,這只是可選的。
redux-actions 封裝了一個 createAction 的函數,用它我們就能寫出更加簡約的 action creator,並且符合 Flux Standard Action 的標準。
以外我們寫 action creator 的時候是這樣:
import * as types from ../constant;export function fetchingLogin() { return { type: types.FETCHING_LOGIN_SUCCESS, }}export function fetchingLoginSuccess(payload) { return { type: types.FETCHING_LOGIN_SUCCESS, payload, }}export function fetchingLoginFailure(payload) { return { type: types.FETCHING_LOGIN_FAILURE, payload, error: true, }}
利用 redux-actions,我們只需要這樣做:
import { createAction } from "redux-actions";import * as types from ../constant;export const fetchingLogin = createAction(types.FETCHING_LOGIN);export const fetchingLoginSuccess = createAction(types.FETCHING_LOGIN_SUCCESS, data => data);export const fetchingLoginFailure = createAction(types.FETCHING_LOGIN_FAILURE, data => data);
這樣就能創建出一個非常標準化的 action 了。
同樣,我們在 reducer 中依舊可以:
import * as actions from ../action;const initState = { status: true,}export default function isLoginReducer(state = initState, action) { switch(action.type) { case actions.CHANGE_IS_LOGIN: { return { ...state, status: action.payload, } } default: { return state; } }}
當然,redux-actions 提供了更簡潔的 API handleActions 可以寫出更加簡約的 renducer:
import { handleActions } from redux-actions;import * as actions from ../action;const initState = { status: true,}const isLoginReducer = handleActions({ [actions.changeIsLogin]: (state, action) => ({ ...state, status: action.payload }),}, initState);export default isLoginReducer;
這樣,教會和你合作的同學使用 redux-actions,就能寫出風格一致的 action。
再也不會盯著亂七八糟的 action 喊出 WTF!
聽說這是最佳實踐
業界關於 redux 的實踐風格已經是保持基本一致了,也就是說按照上面 Redux 4 ways 創建出了風格基本可以解決大部分的問題。
值得注意是關於 React 組件方向最好保持純 React 組件,保持和 Redux 之間不存在耦合。和 Redux 相關的 dispatch 可以放在 container 組件中,通過在 connect 中 mapDispatchToProps 傳給 container,然後調用相應的函數即可:
import { changeIsLogin } from ../../action;const mapDispatchToProps = (dispatch, ownProps) => { return { changeIsLogin: (status) => { dispatch(changeIsLogin(status)); } }}this.props.changeIsLogin(!this.props.isLogin.status)
推薦閱讀:
※用React、Redux、Immutable做俄羅斯方塊
※redux 有什麼缺點?
※如何在非 React 項目中使用 Redux
※redux中所有state都要放在store里嗎?
※redux和react虛擬dom的關係?

