聽說你需要這樣了解 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的關係?