在沒有引入Redux之前,對(duì)于各個(gè)組件之間共享狀態(tài)的處理拆吆,通常是用傳給子組件的事件處理器來(lái)處理肛鹏。狀態(tài)的處理分散到各個(gè)組件代碼中逸邦,很不利于維護(hù)。
引入Redux之后在扰,狀態(tài)的管理集中到一個(gè)地方處理缕减,并且把處理過(guò)程中的不同關(guān)注點(diǎn)對(duì)應(yīng)到Action、Reducer芒珠、Store等幾個(gè)概念桥狡。Action:代表要執(zhí)行的動(dòng)作,Reducer:計(jì)算出新的狀態(tài)皱卓,Store:集中應(yīng)用所有的狀態(tài)裹芝。
能夠集中管理狀態(tài),然后分離關(guān)注點(diǎn)娜汁,從而提高代碼可讀性局雄、可維護(hù)性是使用Redux管理狀態(tài)的好處。
Redux適用場(chǎng)景
多交互存炮、多數(shù)據(jù)源
需要共享某個(gè)組件的狀態(tài)
某個(gè)狀態(tài)需要在任何地方都可以拿到
一個(gè)組件需要改變?nèi)譅顟B(tài)
一個(gè)組件需要改變另一個(gè)組件的狀態(tài)
redux方法
import {
compose,
createStore,
applyMiddleware,
combineReducers,
bindActionCreator
} from 'redux';
Action
代表發(fā)生了什么事炬搭。
action.js
ActionTypes
ActionCreator
action.js
export const SET_AAA;
export const SET_BBB;
// ...other action constants
export function setAAA(aaa) {
return {
type: SET_AAA,
aaa: aaa
};
}
export function setBBB(bbb) {
return {
type: SET_BBB,
bbb: bbb
};
}
// ...other actions
Reducer
表示狀態(tài)如何改變,輸出有最新狀態(tài)穆桂。
注意:只有狀態(tài)的計(jì)算宫盔;不應(yīng)該有副作用;沒有API調(diào)用享完;沒有對(duì)原State的改動(dòng)灼芭。
reducer.js
const initialState = {
aaa: '',
bbb: null,
ccc: null,
// ...
};
export default function(state = initialState, action) {
switch(action.type) {
case "SET_AXXX":
return {...state, aaa: {...xxx}};
case "SEND_XXX":
return {...state, bbb: action.xxx};
case "SET_BXXX":
return {...state, bbb: action.bbb};
case "SET_CXXX":
return {...state, ccc: action.ccc};
default:
return state;
}
}
Store
一個(gè)應(yīng)用只有一個(gè)Store。
store.js
import { createStore } from 'redux';
import { SomeComponent } from './reducers';
let store= createStore(SomeComponent);
store的提供的方法有:
常用redux插件:react-redux般又,redux-thunk彼绷,redux-logger
react-redux
import {
connect,
Provider,
connectAdvanced,
createProvider // 最新代碼為:ReactReduxContext -- Nov 7, 2018
} from 'react-redux';
Example
Vanilla React
ReactDOM.render(
<Provider store={store}>
<MyRootComponent />
</Provider>,
rootEl
)
React Router
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
<Route path="foo" component={Foo}/>
<Route path="bar" component={Bar}/>
</Route>
</Router>
</Provider>,
document.getElementById('root')
)
connect
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
顯示組件(Presentational Components)、容器組件(containers)
State 范式化(摘自官方文檔)
當(dāng)一個(gè)系統(tǒng)較復(fù)雜時(shí)茴迁,有時(shí)數(shù)據(jù)結(jié)構(gòu)會(huì)比較復(fù)雜寄悯,并且有時(shí)部分?jǐn)?shù)據(jù)可能重復(fù)的,有時(shí)還有一些問(wèn)題:
- 當(dāng)數(shù)據(jù)在多處冗余后堕义,需要更新時(shí)猜旬,很難保證所有的數(shù)據(jù)都進(jìn)行更新。
- 嵌套的數(shù)據(jù)意味著 reducer 邏輯嵌套更多、復(fù)雜度更高洒擦。尤其是在打算更新深層嵌套數(shù)據(jù)時(shí)椿争。
- 不可變的數(shù)據(jù)在更新時(shí)需要狀態(tài)樹的祖先數(shù)據(jù)進(jìn)行復(fù)制和更新,并且新的對(duì)象引用會(huì)導(dǎo)致與之 connect 的所有 UI 組件都重復(fù) render熟嫩。盡管要顯示的數(shù)據(jù)沒有發(fā)生任何改變秦踪,對(duì)深層嵌套的數(shù)據(jù)對(duì)象進(jìn)行更新也會(huì)強(qiáng)制完全無(wú)關(guān)的 UI 組件重復(fù) render
正因?yàn)槿绱耍?Redux Store 中管理關(guān)系數(shù)據(jù)或嵌套數(shù)據(jù)的推薦做法是將這一部分視為數(shù)據(jù)庫(kù)掸茅,并且將數(shù)據(jù)按范式化存儲(chǔ)洋侨。
設(shè)計(jì)范式化的 State
范式化的數(shù)據(jù)包含下面幾個(gè)概念:
- 任何類型的數(shù)據(jù)在 state 中都有自己的 “表”。
- 任何 “數(shù)據(jù)表” 應(yīng)將各個(gè)項(xiàng)目存儲(chǔ)在對(duì)象中倦蚪,其中每個(gè)項(xiàng)目的 ID 作為 key希坚,項(xiàng)目本身作為 value。
- 任何對(duì)單個(gè)項(xiàng)目的引用都應(yīng)該根據(jù)存儲(chǔ)項(xiàng)目的 ID 來(lái)完成陵且。
- ID 數(shù)組應(yīng)該用于排序裁僧。
嵌套數(shù)據(jù)范式化
因?yàn)?API 經(jīng)常以嵌套的形式發(fā)送返回?cái)?shù)據(jù),所以該數(shù)據(jù)需要在引入狀態(tài)樹之前轉(zhuǎn)化為規(guī)范化形態(tài)慕购。Normalizr庫(kù)可以幫助你實(shí)現(xiàn)這個(gè)聊疲。你可以定義 schema 的類型和關(guān)系,將 schema 和響應(yīng)數(shù)據(jù)提供給 Normalizr沪悲,他會(huì)輸出響應(yīng)數(shù)據(jù)的范式化變換获洲。輸出可以放在 action 中,用于 store 的更新殿如。
1. State 范式化
2. 管理范式化數(shù)據(jù)
參考文章:
Redux官方網(wǎng)站
Redux官方文檔中文版
阮一峰 - Redux 入門教程(一):基本用法 共3篇
react-dnd 使用實(shí)例
結(jié)合 Immutable.JS 使用 Redux