最近在這段時間瞭解了下redux,下麵簡單記錄下所得。 redux總結 首先,還是用幾句話簡單概括下redux吧,歡迎拍磚。 redux採用的函數式編程方式,基於單項數據流模式管理應用中的狀態。 一個複雜應用對應一個狀態樹,應用模塊之前共用的狀態都交由該狀態樹負責維護。 只能通過觸發action對象 ...
最近在這段時間瞭解了下redux,下麵簡單記錄下所得。
redux總結
首先,還是用幾句話簡單概括下redux吧,歡迎拍磚。
- redux採用的函數式編程方式,基於單項數據流模式管理應用中的狀態。
- 一個複雜應用對應一個狀態樹,應用模塊之前共用的狀態都交由該狀態樹負責維護。
- 只能通過觸發action對象來更新狀態樹。
- redux核心功能只提供簡單的同步方式管理應用狀態,即更新、獲取狀態。其他額外功能通過Middleware方式提供。
下麵,先通過經典應用todos簡單介紹下redux的一些基礎概念
state
state樹中存放一個應用程式內所有共用的數據,一個應用程式有且只有一個state。
todos的state結構為
{
todos: [], // 完整的任務列表
filter: 'SHOW_ALL' // 當前選中的任務過濾條件
}
action
action是唯一種改變state的方式,用來通知reducers修改state。
action是JavaScript plain object,描述“發生了什麼”。
預設action對象內部必須有type欄位(字元串)來表示將要執行的動作。
const ADD_TODO = 'ADD_TODO'
{
type: ADD_TODO,
title: '你個標題黨'
}
actionCreator就是生成action對象的函數,非常純的純函數。
function addTodo(title) {
return {
type: ADD_TODO,
title
}
}
reducer
action對象負責描述”發生了什麼“,那reducer就是執行者,按照action對象的描述,
嚴格更新state。
reducer也是一種純函數,接受action對象和state樹作為參數,生成新的state。
const reducer = (prevState, action) => nextState
這裡reducer採用純函數的意義是保證應用狀態的可預測性,只要傳入參數可知,那結果就可知。
同時nextState並不是直接修改prevState所得,而是在prevState基礎上返回的一個新數組,通過
Object.assign或者immutable.js生成。這樣的目的是方便跟蹤全局應用狀態和回退/撤銷操作,
而且可以在React組件內直接通過shouldComponentUpdate(nextProps, nextState)
進行對比。
所以,千萬不要“玷污”reducer函數:
- 修改傳入參數
- 執行有副作用的函數,如API請求和路由跳轉
- 調用非純函數,如Date.now()或Math.random()
最後,隨著應用的複雜,state樹也會更加龐大,reducer內部的處理邏輯也會更加複雜。很難想象一堆代
碼根據action.type的可能值進行判斷處理。
我們可以通過分解、細化reducers來拆分數據處理邏輯,最後通過redux提供的combineReducers()
API來生成唯一的rootReducer。
const todoApp = combineReducers({
visibilityFilter,
todos
})
store
store就是將action和reducer聯繫在一起的對象。
const = createStore(reducer, initState)
store對象有三種方法,通過這三種方法來維持應用的state。
- 提供getState()方法獲取應用的state
- 提供dispatch(action)方法通知reducer根據action對象更新state
- 提供subscribe(listener)註冊監聽器,監聽state的更新,然後調用listener函數
- 通過subscribe(listener)返回的函數註銷監聽器
至此,redux的核心方面已經說完。你可能發現這個redux似乎和觀察者模式差不多呢。其實,它就是一個觀察者模式。
我們可以通過babel-node執行我們的todos查看redux應用的調用過程。
ps: balel-node是node中安裝babel插件轉換ES6代碼,此時node v6.3.1還不支持export & import用法
上面redux調用流程圖:
redux只是一種應用狀態管理器,我們需要通過react-redux結合React一起使用才能開發出完整的應用。
react-redux
react-redux是redux官方提供的一種綁定react的實現方案。主要提供了兩個api:
<Provider store>
包裹React頂層組件,並且為子組件傳遞Redux store propsconnect([mapStateToProps], [mapDispatchToProps])
connect方法主要有可選參數,具體可參考官網API文檔,不過平常主要使用這兩個
- mapStateToProps(state) 每次state樹更新時,都會被connect調用,返回需要傳遞給子組件的state對象,並被組合到react組件的props中。
- mapDispatchToProps(dispatch): mapDispatchToProps負責返回一個 dispatchProps。dispatchProps 是actionCreator的key和dispatch(action)的組合。
{ addItem: (text) => dispatch(action1), removeItem: (id) => dispatch(action2) }
react組件內部通過this.props.addItem('hello')
調用。
如果有多個action情況,可以通過如下方式進行綁定:
bindActionCreators(actionCreator, store.dispatch)
bindActionCreators 的作用就是將 actionCreator 和 dispatch 組合起來生成 mapDispatchToProps 需要生成的內容。
Middleware(中間件)
在介紹接下來的內容之前,插個已經被問爛的問題:什麼是js閉包和函數柯里化。此處只是簡單的舉個例子說明一下
let test = (a, b) => a + b
let curryTest = a => b => test(a, b)
好,接下來繼續扯我們的,什麼是Middleware
此處,Redux Middleware的設計思路來自於Express,即通過Middleware實現對store.dispatch的封裝,擴展
該函數原有的功能,典型的是實現state日誌記錄的功能。
// 手動記錄logger功能代碼
console.log('dispatching', action)
store.dispatch(action)
console.log('next state', store.getState())
而redux通過Middleware建立一個store.dispatch的鏈條,每層middleware都會接受前一個middleware返回
的next, 然後在進行封裝,返回給後一個middleware調用的next,直到最後一個middleware返回
原始的store.dispatch處理action對象。
例如官網的logger middleware代碼:
const logger = store => next => action => {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
applyMiddleware(...middlewares)源碼:
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, initialState, enhancer) => {
var store = createStore(reducer, initialState, enhancer)
var dispatch = store.dispatch
var chain = []
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
compose(..funcs)源碼
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
} else {
const last = funcs[funcs.length - 1]
const rest = funcs.slice(0, -1)
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}
}
通過源碼我們能發現,applyMiddleware接受的Middlewares數組除了最後一個middleware接受原始的store.dispatch(Array.prototype.reduceRight),其餘middleware都會接受前一個middleware封裝後的next,所以此時的redux流程圖就是下麵這樣:
介紹完redux的middleware,那redux的非同步流程模式也就出來了。官網是通過redux-thunk Middleware實現的,我們看下thunkMiddleware的源碼:
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
這樣store.dispatch就可以接受函數,也就可以將其和網路請求狀態動態綁定在一起了。
具體源碼可參考官網async示例源碼。
本文參考鏈接: