簡單易懂的Redux

簡單易懂的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'}) />

那么問題又來了陨舱,這里面有一個storestore.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 個步驟:

  1. 調用 store.dispatch(action) 乃坤。
  2. Redux store 調用傳入的 reducer 函數苛让。
  3. rootReducer 應該把多個子 reducer 輸出合并成一個單一的 state 樹。
  4. Redux store 保存了rootReducer 返回的完整 state 樹湿诊。

這些其實就是createStore中的一部分狱杰,建議可以直接去redux-createStore.js讀一下,代碼挺簡單的厅须。之前也介紹過浦旱。dispatch就是調用了reducer而已。currentReducer是reducers的合集九杂。
一般可以通過combineReducers函數組合起來颁湖。有空可以講一下原理。然后store里面就是全部的state了例隆。

本文需要結合redux官方文檔看甥捺,感覺redux的中文文檔有些翻譯的不是特別好,有條件還是去看下英文文檔镀层,不過講的很潦草镰禾。實踐才能知道。

redux數據流

附錄:
redux官方文檔:中文:http://www.redux.org.cn/
英文: http://redux.js.org/
redux源代碼:https://github.com/reactjs/redux

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末唱逢,一起剝皮案震驚了整個濱河市吴侦,隨后出現的幾起案子,更是在濱河造成了極大的恐慌坞古,老刑警劉巖备韧,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異痪枫,居然都是意外死亡织堂,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門奶陈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來易阳,“玉大人,你說我怎么就攤上這事吃粒×拾常” “怎么了?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵徐勃,是天一觀的道長事示。 經常有香客問我,道長疏旨,這世上最難降的妖魔是什么很魂? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮檐涝,結果婚禮上遏匆,老公的妹妹穿的比我還像新娘。我一直安慰自己谁榜,他們只是感情好幅聘,可當我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著窃植,像睡著了一般帝蒿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上巷怜,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天葛超,我揣著相機與錄音暴氏,去河邊找鬼。 笑死绣张,一個胖子當著我的面吹牛答渔,可吹牛的內容都是我干的。 我是一名探鬼主播侥涵,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼沼撕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了芜飘?” 一聲冷哼從身側響起务豺,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嗦明,沒想到半個月后笼沥,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡招狸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年敬拓,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片裙戏。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡乘凸,死狀恐怖,靈堂內的尸體忽然破棺而出累榜,到底是詐尸還是另有隱情营勤,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布壹罚,位于F島的核電站葛作,受9級特大地震影響,放射性物質發(fā)生泄漏猖凛。R本人自食惡果不足惜赂蠢,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望辨泳。 院中可真熱鬧虱岂,春花似錦、人聲如沸菠红。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽试溯。三九已至蔑滓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背键袱。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工燎窘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人杠纵。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓荠耽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親比藻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內容