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

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