聽說你需要這樣了解 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格式為如下,包括一個 typetext(可以為空):

{ 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 見: jsfiddle.net/rccoder/sh

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 見:jsfiddle.net/rccoder/sh

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 見 jsfiddle.net/rccoder/sh

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 函數。(i5ting.github.io/asynch)

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 => stateactions

state => state 就是 mapStateToProps 的函數,他會監聽 store 的變化,只要 store 變化,這個函數就會被調用。mapStateToProps 返回一個對象,這個對象會與組件的 props 合併。mapStateToProps 接收兩個參數 stateownProps,通常情況下我們只需要第一個參數,如果有第二個參數 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 詳細的說明,還是建議閱讀官方文檔:github.com/reactjs/reac

這裡有個小聰明可以玩,如果你已經用上了 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 就會處理 subscribegetState,傳給相應的組件,同時也保持了比較高的性能。

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的關係?

TAG:Redux | React | 數據流 |