目錄
- 什么是Redux
- Redux 優(yōu)點
- Redux 的三個基本原則
- Redux 有那幾部分組成
- Action
- reducer
- Store
- 異步Action
- 配置React Native與Redux開發(fā)
- 第一步
- 第二步
- 第三步

什么是Redux
Redux 是 JavaScript 狀態(tài)容器,提供可預測化的狀態(tài)管理,可以讓你構建一致化的應用,運行于不同的環(huán)境(客戶端、服務器、原生應用),并且易于測試。數(shù)據(jù)是單向流動的。

工作流程
- 用戶(操作View)發(fā)出Action,發(fā)出方式就用到了dispatch方法;
- 然后,Store自動調用Reducer,并且傳入兩個參數(shù)(當前State和收到的Action),Reducer會返回新的State,如果有Middleware,Store會將當前State和收到的Action傳遞給Middleware,Middleware會調用Reducer 然后返回新的State;
- State一旦有變化,Store就會調用監(jiān)聽函數(shù),來更新View;
Middleware:可以讓你在reducer執(zhí)行前與執(zhí)行后進行攔截并插入代碼,來達到操作action和Store的目的,這樣一來就很容易實現(xiàn)靈活的日志打印、錯誤收集、API請求、路由等操作。
Redux優(yōu)點
- 可預測: 始終有一個唯一的準確的數(shù)據(jù)源(single source of truth)就是store,通過actions和reducers來保證整個應用狀態(tài)同步,做到絕不混亂
- 易維護: 具備可預測的結果和嚴格的組織結構讓代碼更容易維護
- 易測試: 編寫可測試代碼的首要準則是編寫可以僅做一件事并且獨立的小函數(shù)(single responsibility principle),Redux的代碼幾乎全部都是這樣的函數(shù):短小·純粹·分離
Redux 的三個基本原則
- 單一數(shù)據(jù)源:整個應用的 state 被儲存在一棵 object tree 中,并且這個 object tree 只存在于唯一一個 store 中;
- State 是只讀的:唯一改變 state 的方法就是觸發(fā) action,action 是一個用于描述已發(fā)生事件的普通對象;
- 使用純函數(shù)來執(zhí)行修改:為了描述 action 如何改變 state tree ,你需要編寫 reducers;
Redux 有那幾部分組成
action:action就是一個描述發(fā)生什么的對象;
reducer:形式為 (state, action) => state 的純函數(shù),功能是根據(jù)action 修改state 將其轉變成下一個 state;
store:用于存儲state,你可以把它看成一個容器,整個應用只能有一個store。
Action
Action 是把數(shù)據(jù)從應用傳到 store的有效載荷。它是 store數(shù)據(jù)的唯一來源,也就是說要改變store中的state就需要觸發(fā)一個action。
Action 本質上一個普通的JavaScript對象。action 內必須使用一個字符串類型的type字段來表示將要執(zhí)行的動作,除了 type 字段外,action 對象的結構完全由你自己決定。多數(shù)情況下,type 會被定義成字符串常量。當應用規(guī)模越來越大時,建議使用單獨的模塊或文件來存放 action。
import { ADD_TODO, REMOVE_TODO } from '../actionTypes'
//action
{
type: ADD_TODO,
text: 'Build my first Redux app'
}
提示:使用單獨的模塊或文件來定義 action type 常量并不是必須的,甚至根本不需要定義。對于小應用來說,使用字符串做 action type 更方便些。不過,在大型應用中把它們顯式地定義成常量還是利大于弊的。
reducer
reducer是根據(jù)action 修改state 將其轉變成下一個 state,記住 actions 只是描述了有事情發(fā)生了這一事實,并沒有描述應用如何更新 state。
(previousState, action) => newState
保持 reducer 純凈非常重要。永遠不要在 reducer 里做這些操作
- 修改傳入?yún)?shù);
- 執(zhí)行有副作用的操作,如 API 請求和路由跳轉;
- 調用非純函數(shù),如 Date.now() 或 Math.random()。
提示:reducer 是純函數(shù)。它僅僅用于計算下一個 state。它應該是完全可預測的:多次傳入相同的輸入必須產生相同的輸出。它不應做有副作用的操作,如 API 調用或路由跳轉。這些應該在 dispatch action 前發(fā)生。
拆分reducer:根據(jù)不同模塊的劃分去查分reducer,減少冗長,讓代碼簡潔。
合并reducer:經過上述的步驟我們將一個大的reducer拆分成了不同的小的reducer,但redux原則是只允許一個根reducer,接下來我們需要將這幾個小的reducer聚合到一個跟reducer中。
這里我們需要用到Redux 提供的combineReducers(reducers)。
combineReducers() 所做的只是生成一個函數(shù),這個函數(shù)來調用你的一系列 reducer,每個 reducer 根據(jù)它們的 key 來篩選出 state 中的一部分數(shù)據(jù)并處理,然后這個生成的函數(shù)再將所有 reducer 的結果合并成一個大的對象。沒有任何魔法。正如其他 reducers,如果 combineReducers()中包含的所有 reducers 都沒有更改 state,那么也就不會創(chuàng)建一個新的對象
Store
是存儲state的容器,Store 會把兩個參數(shù)(當前的 state 樹和 action)傳入 reducer。
store 有以下職責:
- 維持應用的 state;
- 提供 getState() 方法獲取 state;
- 提供 dispatch(action)方法更新 state:我們可以在任何地方調用 store.dispatch(action),包括組件中、XMLHttpRequest 回調中、甚至定時器中;
- 通過 subscribe(listener) 注冊監(jiān)聽器;
- 通過 subscribe(listener) 返回的函數(shù)注銷監(jiān)聽器。
我們使用 combineReducers() 將多個 reducer 合并成為一個?,F(xiàn)在我們通過Redux的 createStore()來創(chuàng)建一個Store。
import { createStore } from 'redux'
import todoApp from './reducers'
let store = createStore(todoApp)
異步Action
上文中所講的Action都是基于同步實現(xiàn)的,那么對于網(wǎng)絡請求數(shù)據(jù)庫加載等應用場景同步Action顯然是不適用的,對此我們需要用到異步Action。
我們可將異步Action簡答理解為:在Action中進行異步操作等操作返回后再dispatch一個action。
為了使用異步
action我們需要引入redux-thunk庫,redux-thunk是為Redux提供異步action支持的中間件。
使用redux-thunk
npm install --save redux-thunk
import thunk from 'redux-thunk'
let middlewares = [
thunk
]
//添加異步中間件redux-thunk
let createAppStore = applyMiddleware(...middlewares)(createStore)
創(chuàng)建異步Action
export function onSearch(inputKey, token, popularKeys) {
return dispatch => {
dispatch({type: Types.SEARCH_REFRESH});
fetch(genFetchUrl(inputKey)).then(response => {//如果任務取消,則不做任何處理
return checkCancel(token) ? response.json() : null;
}).then(responseData => {
if (!checkCancel(token, true)) {//如果任務取消,則不做任何處理
return
}
if (!responseData || !responseData.items || responseData.items.length === 0) {
dispatch({type: Types.SEARCH_FAIL, message: inputKey + '什么都沒找到'});
return
}
let items = responseData.items;
getFavoriteKeys(inputKey, dispatch, items, token, popularKeys);
}).catch(e => {
console.log(e);
dispatch({type: Types.SEARCH_FAIL, error: e});
})
}
}
異步數(shù)據(jù)流
默認情況下,createStore() 所創(chuàng)建的 Redux store 沒有使用 middleware,所以只支持 同步數(shù)據(jù)流。
你可以使用 applyMiddleware() 來增強 createStore()。它可以幫助你用簡便的方式來描述異步的 action。
像 redux-thunk 或 redux-promise 這樣支持異步的 middleware 都包裝了 store 的 dispatch() 方法,以此來讓你 dispatch 一些除了 action 以外的其他內容,例如:函數(shù)或者 Promise。你所使用的任何 middleware 都可以以自己的方式解析你 dispatch 的任何內容,并繼續(xù)傳遞 actions 給下一個 middleware。比如,支持 Promise 的 middleware 能夠攔截 Promise,然后為每個 Promise 異步地 dispatch 一對 begin/end actions。
當 middleware 鏈中的最后一個 middleware 開始 dispatch action 時,這個 action 必須是一個普通對象;
配置React Native與Redux開發(fā)
第一步:在使用 React Navigation 的項目中,想要集成 redux 就必須要引入 react-navigation-redux-helpers 這個庫。
第二步:配置Navigator
/**
* 1.初始化react-navigation與redux的中間件,
* 該方法的一個很大的作用就是為reduxifyNavigator的key設置actionSubscribers(行為訂閱者)
* 設置訂閱者@https://github.com/react-navigation/react-navigation-redux-helpers/blob/master/src/middleware.js#L29
* 檢測訂閱者是否存在@https://github.com/react-navigation/react-navigation-redux-helpers/blob/master/src/middleware.js#L97
* @type {Middleware}
*/
export const middleware = createReactNavigationReduxMiddleware(
'root',
state => state.nav
);
/**
* 2.將根導航器組件傳遞給 reduxifyNavigator 函數(shù),
* 并返回一個將navigation state 和 dispatch 函數(shù)作為 props的新組件;
* 注意:要在createReactNavigationReduxMiddleware之后執(zhí)行
*/
const AppWithNavigationState = reduxifyNavigator(RootNavigator, 'root');
/**
* State到Props的映射關系
* @param state
*/
const mapStateToProps = state => ({
state: state.nav,//v2
});
/**
* 3.連接 React 組件與 Redux store
*/
export default connect(mapStateToProps)(AppWithNavigationState);
第三步配置Reducer
//1.指定默認state
const navState = RootNavigator.router.getStateForAction(RootNavigator.router.getActionForPathAndParams(rootCom));
/**
* 2.創(chuàng)建自己的 navigation reducer,
*/
const navReducer = (state = navState, action) => {
const nextState = RootNavigator.router.getStateForAction(action, state);
// 如果`nextState`為null或未定義,只需返回原始`state`
return nextState || state;
};
/**
* 3.合并reducer
* @type {Reducer<any> | Reducer<any, AnyAction>}
*/
const index = combineReducers({
nav: navReducer,
theme: theme,
popular: popular,
trending: trending,
favorite: favorite,
language: language,
search: search,
});
export default index;
第四步:配置store
/**
* 自定義log中間件
* https://cn.redux.js.org/docs/advanced/Middleware.html
* @param store
*/
const logger = store => next => action => {
if (typeof action === 'function') {
console.log('dispatching a function');
} else {
console.log('dispatching ', action);
}
const result = next(action);
console.log('nextState ', store.getState());
return result;
};
const middlewares = [
middleware,
logger,
thunk,
];
/**
* 創(chuàng)建store
*/
export default createStore(reducers, applyMiddleware(...middlewares));