簡單易懂的Redux
根據redux官方文檔來說勇吊,可以將redux分為三個核心部分照棋。
- actions
- reducers
- store
1. actions
其中眶熬,actions主要負責定義javascript object信不,這個js類是用在application和store之間傳遞數據的屈溉。簡單的說贱迟,就是把一個操作抽象成一個類姐扮。舉個例子,在web頁面上的一個button衣吠,用戶點擊button茶敏,會觸發(fā)一個click事件。這個click事件缚俏,我們可以抽象成一個actions類惊搏。我們給它一個type屬性贮乳,來唯一定義這個事件,比如說胀屿,就叫buttonclick
塘揣。那么這個actions就定義好了。
{
type: 'buttonclick',
}
這個js類就是我們的單擊button的actions宿崭。
對于其他的actions亲铡,例如text的改變也可以是一個actions,會包括text的值葡兑,那么就會變成下面的樣子:
{
type: 'textchange',
textvalue: 'mytextvalue',
}
類似這樣奖蔓,就是抽象出來的兩個類。但是讹堤,這樣的actions吆鹤,在textvalue改變的時候,是需要再次創(chuàng)建的洲守,也就是說里面的屬性一變就要創(chuàng)建一個新的actions疑务。所以單純的actions就沒法滿足這一點。就有了Actions creator梗醇。
Actions creator
Actions creator 就是創(chuàng)建actions的function函數知允。主要作用就是返回一個actions。一般情況下叙谨,如下:
export function textChange(textvalue) {
return {
type: 'textchange',
textvalue,
}
}
這樣就是一個actions creator温鸽,用來創(chuàng)建上面的text actions,可以復用手负,比如說:
textChange('hello');
就創(chuàng)建了一個{type: 'textchange', textvalue: 'hello'}
的actions涤垫。Actions creator可以在各種地方創(chuàng)建類似的actions。然后通過dispatch調用actions竟终。dipatch的作用之后會講蝠猬。
2. reducers
我們現在定義好了每個操作的actions,把每個操作都抽象成了一個類统捶。還少了幾個部分:
- actions如何和前端的views或者其他application相關綁定起來吱雏,來表明這個操作就是對應這個actions
- 以及觸發(fā)了這個actions會導致數據發(fā)生什么變化
我們先把數據變化的部分也抽象出來講解,中間的數據傳遞和綁定之后在介紹瘾境。
觸發(fā)actions導致的數據變化可以簡寫為:
原來的狀態(tài)(state) + action => 新狀態(tài)(new state)
reducer就是要定義這一過程。因此reducer一般都是這樣的:
reducer(state,action){ return newState;}
所以我們要定義一個reducer镰惦,來通過點擊按鈕+1迷守,那么這個數據變化就也可以實現成:
export function addReducers(state=0, action) {
switch(action.type) {
case 'buttonclick':
return state + 1;
default:
return state;
}
}
這樣就定義好了一個reducer,它表明旺入,當action類型是buttonclick
的時候兑凿,state就會加1凯力。我們可以用store.dispatch
去使用這個reducer更新我們的state值。
因此礼华,reducer的作用就是用來指明應用如何更新state咐鹤。
3. store 和 store.dispatch
根據1,我們只定義了actions圣絮,但是這些actions只是我們抽象出來的一些js類祈惶,就和其他代碼一樣,抽象了一個類出來扮匠,卻沒有把它和我們的頁面連起來捧请,頁面不知道哪個actions對應了什么操作,點擊按鈕也不會真的觸發(fā)這個actions棒搜。
因此我們需要把這些actions和我們的頁面組件連起來疹蛉。
比如說,在我們的button上添加一個onClick
屬性力麸,使它調用 dispatch
函數可款,來觸發(fā)這個action。大概是下面這樣克蚂。
<Button onClick={store.dispatch({type: 'buttonclick'})/>
也可以通過之前定義的textChange這個action creator來創(chuàng)建action闺鲸,并使用store.dispatch調用reducer。
<Text onChange={store.dispatch(textChange('hello')) ... />
// is same as
<Text onChange={store.dispatch({type: 'textchange', textvalue: 'hello'}) />
那么問題又來了陨舱,這里面有一個store
和store.dispatch
都是什么翠拣。
我們先介紹一下store,store就是連接actions和reducers的游盲。actions指定了我們做了什么操作误墓,reducers說明了我們做了這些操作之后數據發(fā)生了什么變化。而store則用來把它們連在一起益缎。
簡單來說谜慌,store提供了以下功能:
? 維持應用的 state;(英文原文是Holds application state;個人理解吧莺奔,就是store中有一個state屬性欣范,可以通過getState()方法獲取。)
? 提供 getState() 方法獲取 state令哟;
? 提供 dispatch(action) 方法更新 state恼琼;
? 通過 subscribe(listener) 注冊監(jiān)聽器;
? 通過 subscribe(listener) 返回的函數注銷監(jiān)聽器。
這個就要涉及到redux的數據流了屏富。簡單來說晴竞,當用戶對頁面進行操作時就會有相關的事件,我們將actions綁定在這些事件上狠半,然后這些事件觸發(fā)的時候噩死,就可以通過store.dispatch
來調用颤难,然后就會進行下面的操作,這里是redux中createStore的部分源碼已维。簡單瀏覽一下行嗤。
export default function createStore(reducer, preloadedState, enhancer) {
// 這里的enhancer參數主要是一些第三方的功能,如midderware, time travel, persistence,可以為undefined
...
let currentReducer = reducer
let currentState = preloadedState // 初始化currentState, 可以為undefined
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false
...
function getState() {
return currentState // getState()返回的是當前的state值
}
...
function dispatch(action) {
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
} // 判定dispatch的action是不是純函數垛耳,不是純函數報錯
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
} // 判定action的type是否存在
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
} // 這個isDispatching會在store創(chuàng)建的時候定義為false
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
} // currentState只能通過currentReducer更新栅屏,調用了這個reducer,返回新的state值艾扮,用來更新currentState
const listeners = currentListeners = nextListeners
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
...
通過上面的代碼既琴,發(fā)現currentState只能通過currentReducer來更新狀態(tài)。并且通過dispatch(action)
這個操作泡嘴,會執(zhí)行currentState = currentReducer(currentState, action)
這個就是更新了state甫恩。所以,當我們想要用store的時候酌予,先創(chuàng)建一個store磺箕。也就是
const store = createStore(reducers)
然后這個store就可以被使用了。
先在index.js中注明這個store抛虫,類似下面這樣松靡,就是把store傳給了App這個component,在App中就可以使用store了建椰。也可以使用store的兩個函數getState()和dispatch()雕欺。
ReactDOM.render(
<App store={store} />,
document.getElementById('root')
);
因此,如果我們的上面的button和text是在App這個Component中定義的棉姐,那么現在上面的
<Button onClick={store.dispatch({type: 'buttonclick'})/>
就有效了屠列,這個store.dispatch就會去改變state的狀態(tài)。
這就是三者的關系了伞矩。
簡單的數據流
用戶相關操作 ---> 抽象成actions
用戶操作導致的state變化 ---> 抽象成reducers
state + action ---> newState
store.dispatch主要實現了: state + action ---> newState
因此簡單的數據流可以在redux的官方文檔里面看到: [redux-flows]
這里不太想做過多介紹笛洛,因為上面都講過了。
Redux 應用中數據的生命周期遵循下面 4 個步驟:
- 調用 store.dispatch(action) 乃坤。
- Redux store 調用傳入的 reducer 函數苛让。
- rootReducer 應該把多個子 reducer 輸出合并成一個單一的 state 樹。
- Redux store 保存了rootReducer 返回的完整 state 樹湿诊。
這些其實就是createStore中的一部分狱杰,建議可以直接去redux-createStore.js讀一下,代碼挺簡單的厅须。之前也介紹過浦旱。dispatch就是調用了reducer而已。currentReducer是reducers的合集九杂。
一般可以通過combineReducers函數組合起來颁湖。有空可以講一下原理。然后store里面就是全部的state了例隆。
本文需要結合redux官方文檔看甥捺,感覺redux的中文文檔有些翻譯的不是特別好,有條件還是去看下英文文檔镀层,不過講的很潦草镰禾。實踐才能知道。
附錄:
redux官方文檔:中文:http://www.redux.org.cn/
英文: http://redux.js.org/
redux源代碼:https://github.com/reactjs/redux