Redux入門學(xué)習(xí)系列教程(一)
Redux入門學(xué)習(xí)系列教程(二)
Redux入門學(xué)習(xí)系列教程(三)
Redux入門學(xué)習(xí)系列教程(四)
作者結(jié)合文檔,給出入門Redux學(xué)習(xí) Demo示例
https://github.com/guangqiang-liu/react-native-reduxDemo
本教程主要講解Redux的核心 API 以及 工作流程
什么是Redux
Redux 是 JavaScript 狀態(tài)容器,提供可預(yù)測化的狀態(tài)管理??梢宰屇銟?gòu)建一致化的應(yīng)用,運(yùn)行于不同的環(huán)境(客戶端、服務(wù)器、原生應(yīng)用),并且易于測試。不僅于此,它還提供 超爽的開發(fā)體驗(yàn),比如有一個(gè)時(shí)間旅行調(diào)試器可以編輯后實(shí)時(shí)預(yù)覽
安裝Redux
npm install redux --save
多數(shù)情況下,我們還需要使用 React 綁定庫和開發(fā)者工具。
npm install redux-devtools --save-dev
核心API
- Action
- Reducer
- Store
Redux三大原則
- 單一數(shù)據(jù)源
整個(gè)應(yīng)用的 state 被儲(chǔ)存在一棵 object tree 中,并且這個(gè) object tree 只存在于一個(gè) 唯一的store 中。
- State 只讀
惟一改變 state 的方法就是觸發(fā) action,action 是一個(gè)用于描述已發(fā)生事件的普通對(duì)象。
- 使用純函數(shù)來執(zhí)行修改
為了描述 action 如何改變 state tree ,你需要編寫 reducers。
Action
- Action官方解釋:把數(shù)據(jù)從應(yīng)用傳到 store 的有效載荷。它是 store 數(shù)據(jù)的唯一來源。一般來說你會(huì)通過
store.dispatch()將 action 傳到 store。 - 其實(shí)Action就是一個(gè)普通的對(duì)象,其中的
type屬性是必須的,表示 Action 的名稱。其他屬性可以自由設(shè)置,參照 Flux 標(biāo)準(zhǔn) Action 獲取關(guān)于如何構(gòu)造 action 的建議。
redux約定 Action 內(nèi)使用一個(gè)字符串類型的type字段來表示將要執(zhí)行的動(dòng)作名稱。
{
type: 'ADD_TODO'
}
除了type 之外,Action還可以攜帶需要的數(shù)據(jù)。
{
type: 'ADD_ITEM',
text: 'Learn Redux'
}
Action Creator
View 要發(fā)送多少種消息,就會(huì)有多少種 Action。如果都手寫,會(huì)很麻煩??梢远x一個(gè)函數(shù)來生成 Action,這個(gè)函數(shù)就叫 Action Creator。
const ADD_TODO = '添加 TODO'
function addTodo(text) {
return {
type: ADD_TODO,
text:text
}
}
const action = addTodo('Learn Redux')
上面代碼中,addTodo函數(shù)就是一個(gè) Action Creator,在 Redux 中的 Action Creator 只是簡單的返回一個(gè) action而已
在 傳統(tǒng)的 Flux 實(shí)現(xiàn)中,當(dāng)調(diào)用 Action Creator時(shí),一般會(huì)觸發(fā)一個(gè) dispatch
function addTodoWithDispatch(text) {
const action = {
type: ADD_TODO,
text:text
}
dispatch(action)
}
在Redux 中只需把 Action Creator的結(jié)果傳給 dispatch() 方法即可發(fā)起一次 dispatch 過程。
dispatch(addTodo(text))
或者創(chuàng)建一個(gè)被綁定的 Action Creator 來自動(dòng)觸發(fā)dispatch
const bindAddTodo = (text) => dispatch(addTodo(text))
然后直接調(diào)用這個(gè)函數(shù)即可完成一次dispatch過程
bindAddTodo(text)
注意:
store 里能直接通過 store.dispatch() 調(diào)用dispatch()方法,但是多數(shù)情況下我們選擇使用 react-redux 提供的 connect() 幫助器來調(diào)用。bindActionCreators() 可以自動(dòng)把多個(gè) action 創(chuàng)建函數(shù) 綁定到 dispatch() 方法上
Reducer
Store 收到 Action 以后,必須給出一個(gè)新的 State,這樣 View 才會(huì)發(fā)生變化。這種 State 的計(jì)算過程就叫做 Reducer。
Reducer 就是一個(gè)普通的函數(shù)
當(dāng)被Redux調(diào)用的時(shí)候會(huì)給Reducer傳遞兩個(gè)參數(shù):State 和 Action
它會(huì)根據(jù) Action 的type屬性來對(duì)舊的 State 進(jìn)行操作,返回新的State
const defaultState = 10
const reducer = (state = defaultState, action) => {
switch (action.type) {
case Constants.INCREASE:
return state + 1
case Constants.DECREASE:
return state - 1
default:
return state
}
}
const state = reducer(10, {
type: Constants.INCREASE
})
上面代碼中,reducer函數(shù)收到名為INCREASE的 Action 后,就返回一個(gè)新的 State,作為加法的計(jì)算結(jié)果。
實(shí)際開發(fā)中,Reducer 函數(shù)不用像上面這樣手動(dòng)去調(diào)用,store.dispatch方法會(huì)觸發(fā) Reducer 的自動(dòng)調(diào)用,為此,Store 需要知道 Reducer 函數(shù),做法就是在生成 Store 的時(shí)候,將 Reducer 傳入到createStore函數(shù)中。
import { createStore } from 'redux'
import reducer from '../reducer'
// 創(chuàng)建store
const store = createStore(reducer)
上面代碼中,createStore接受 Reducer 作為參數(shù),生成一個(gè)新的 Store。這樣以后每當(dāng)store.dispatch發(fā)送過來一個(gè)新的 Action,就會(huì)自動(dòng)調(diào)用 Reducer,得到新的 State
Reducer 的拆分
真正開發(fā)項(xiàng)目的時(shí)候State會(huì)涉及很多功能,在一個(gè)Reducer函數(shù)中處理所有邏輯會(huì)非?;靵y,所以需要拆分成多個(gè)子Reducer,每個(gè)子Reducer只處理它管理的那部分State數(shù)據(jù)。然后在由一個(gè)主rootReducers來專門管理這些子Reducer。
Redux提供了一個(gè)方法:combineReducers專門來管理這些子Reducer
import {createStore, combineReducers} from 'redux'
const list = (state = [], action) => {
switch (action.type) {
case ADD_ITEM:
return [createItem(action.text), ...state]
default:
return state
}
}
const counter = (state = defaultState, action) => {
switch (action.type) {
case Constants.INCREASE:
return state + 1
case Constants.DECREASE:
return state - 1
default:
return state
}
}
let rootReducers = combineReducers({list, counter})
combineReducers 生成了一個(gè)類似于Reducer的函數(shù)。為什么是類似于尼,因?yàn)樗皇钦嬲腞educer,它只是一個(gè)調(diào)用Reducer的函數(shù),只不過它接收的參數(shù)與真正的Reducer一模一樣
combineReducers 核心源碼解讀
function combineReducers(reducers) {
// 過濾reducers,把非function類型的過濾掉~
var finalReducers = pick(reducers, (val) => typeof val === 'function');
var defaultState = mapValues(finalReducers, () => undefined);
return function combination(state = defaultState, action) {
// finalReducers 是 reducers
var finalState = mapValues(finalReducers, (reducer, key) => {
// state[key] 是當(dāng)前Reducer所對(duì)應(yīng)的State,可以理解為當(dāng)前的State
var previousStateForKey = state[key];
var nextStateForKey = reducer(previousStateForKey, action);
return nextStateForKey;
});
// finalState 是 Reducer的key和stat的組合。。
}
}
從上面的源碼可以看出,combineReducers生成一個(gè)類似于Reducer的函數(shù)combination。
注意:
當(dāng)使用combination的時(shí)候,combination會(huì)把所有子Reducer都執(zhí)行一遍,子Reducer通過action.type 匹配操作,因?yàn)槭菆?zhí)行所有子Reducer,所以如果兩個(gè)子Reducer匹配的action.type是一樣的,那么都會(huì)匹配成功。
Store
- Store 就是保存數(shù)據(jù)的地方,你可以把它看成一個(gè)容器。整個(gè)應(yīng)用只能有一個(gè) Store。
- Redux 提供createStore這個(gè)函數(shù),用來生成 Store。
- 再次強(qiáng)調(diào)一下: Redux 應(yīng)用只有一個(gè)單一的 store。當(dāng)需要拆分?jǐn)?shù)據(jù)處理邏輯時(shí),你應(yīng)該使用 reducer 組合 而不是創(chuàng)建多個(gè) store。
- 根據(jù)已有的 reducer 來創(chuàng)建 store 是非常容易的。在前一個(gè)章節(jié)中,我們使用 combineReducers() 將多個(gè) reducer 合并成為一個(gè)?,F(xiàn)在我們將其導(dǎo)入,并傳遞給createStore函數(shù)。
import { createStore } from 'redux'
import reducer from '../reducer'
const store = createStore(reducer)
Store提供暴露出四個(gè)API方法
- store.getState(): 獲取應(yīng)用當(dāng)前State。
- store.subscribe():添加一個(gè)變化監(jiān)聽器。
- store.dispatch():分發(fā) action。修改State。
- store.replaceReducer():替換 store 當(dāng)前用來處理 state 的 reducer。
import { createStore } from 'redux'
let { subscribe, dispatch, getState, replaceReducer} = createStore(reducer)
下面是createStore方法的一個(gè)簡單實(shí)現(xiàn),可以了解一下 Store 是怎么生成的。
const createStore = (reducer) => {
let state;
let listeners = [];
const getState = () => state;
const dispatch = (action) => {
state = reducer(state, action);
listeners.forEach(listener => listener());
};
const subscribe = (listener) => {
listeners.push(listener);
return () => {
listeners = listeners.filter(l => l !== listener);
}
};
dispatch({});
return { getState, dispatch, subscribe };
}
發(fā)起 Actions
現(xiàn)在我們已經(jīng)創(chuàng)建好了 store ,讓我們來驗(yàn)證一下!雖然還沒有界面,我們已經(jīng)可以測試數(shù)據(jù)處理邏輯了。
import { addTodo, toggleTodo, setVisibilityFilter, VisibilityFilters } from './actions'
// 打印初始狀態(tài)
console.log(store.getState())
// 每次 state 更新時(shí),打印日志
// 注意 subscribe() 返回一個(gè)函數(shù)用來注銷監(jiān)聽器
let unsubscribe = store.subscribe(() =>
console.log(store.getState())
)
// 發(fā)起一系列 action
store.dispatch(addTodo('Learn about actions'))
store.dispatch(addTodo('Learn about reducers'))
store.dispatch(addTodo('Learn about store'))
store.dispatch(toggleTodo(0))
store.dispatch(toggleTodo(1))
store.dispatch(setVisibilityFilter(VisibilityFilters.SHOW_COMPLETED))
// 停止監(jiān)聽 state 更新
unsubscribe()
可以看到 store 里的 state 是如何變化的:

可以看到,在還沒有開發(fā)界面的時(shí)候,我們就可以定義程序的行為。而且這時(shí)候已經(jīng)可以寫 reducer 和 action 創(chuàng)建函數(shù)的測試。不需要模擬任何東西,因?yàn)樗鼈兌际羌兒瘮?shù)。只需調(diào)用一下,對(duì)返回值做斷言,寫測試就是這么簡單。
Redux 工作流

- 首先,用戶發(fā)出 Action
store.dispatch(action)
- 然后,Store 自動(dòng)調(diào)用 Reducer,并且傳入兩個(gè)參數(shù):當(dāng)前 State 和收到的 Action。 Reducer 會(huì)返回新的 State
let nextState = todoApp(previousState, action)
- State 一旦有變化,Store 就會(huì)調(diào)用監(jiān)聽函數(shù)
// 設(shè)置監(jiān)聽函數(shù)
store.subscribe(listener)
- listener可以通過store.getState()得到當(dāng)前狀態(tài)。如果使用的是 React,這時(shí)可以觸發(fā)重新渲染 View
function listerner() {
let newState = store.getState();
component.setState(newState);
}
參考文獻(xiàn)
阮一峰講解redux
http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html
http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_two_async_operations.html
http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_three_react-redux.html
Redux中文文檔
Redux其他系列教程
https://github.com/lewis617/react-redux-tutorial
https://github.com/kenberkeley/redux-simple-tutorial
Redux入門學(xué)習(xí)系列教程(一)
Redux入門學(xué)習(xí)系列教程(二)
Redux入門學(xué)習(xí)系列教程(三)
Redux入門學(xué)習(xí)系列教程(四)
總結(jié)
本系列教程是參照阮老師的Redux入門教程文章和Redux中文文檔進(jìn)行的整合和拓展。更多更詳細(xì)的Redux使用方式請(qǐng)參照上面的參考文獻(xiàn)。
福利時(shí)間
- 作者React Native開源項(xiàng)目OneM地址(按照企業(yè)開發(fā)標(biāo)準(zhǔn)搭建框架設(shè)計(jì)開發(fā)):https://github.com/guangqiang-liu/OneM (歡迎小伙伴們 star)
- 作者簡書主頁:包含50多篇RN開發(fā)相關(guān)的技術(shù)文章http://www.itdecent.cn/u/023338566ca5 (歡迎小伙伴們:多多關(guān)注,多多點(diǎn)贊)
- 作者React Native QQ技術(shù)交流群:620792950 歡迎小伙伴進(jìn)群交流學(xué)習(xí)
- 友情提示:在開發(fā)中有遇到RN相關(guān)的技術(shù)問題,歡迎小伙伴加入交流群(620792950),在群里提問、互相交流學(xué)習(xí)。交流群也定期更新最新的RN學(xué)習(xí)資料給大家,謝謝支持!