通俗易懂的Redux瞭解下

Redux讓我腦仁疼,感受搞不定他。由於Redux對我而言太抽象了,因此我用通俗易懂地方法去思考Redux,感受可以理解Redux一些了。node

本文要點:git

  • action
    • 配置行爲
    • store.dispatch(action)
  • reducer
    • 純函數
    • 返回新的state
    • createStore(reducer)
  • store
    • getStore
    • dispatch
    • subscribe
  • combineReducers

本文代碼倉庫,在此文件夾之中。es6

講道理我要用import和export的寫法來學習Redux,可是node有些es6仍是不支持的,爲了簡單演示,我就用requre和module.exports來演示。(否則我還得講解下babel轉換es6的知識點)github

在開始以前,咱們是否是應該安裝一個redux,而後才能開始愉快地玩耍。npm

npm install --save redux
複製代碼

action 和 reducer 一對好基友

用Redux建立全局應用數據管理store以前,咱們須要建立兩個類型的JS文件,一個是action一個是reducer,這兩個類型的功能我我的認爲很容易搞混。不知道哪一個是幹什麼,常常忘記用法。那我只能經過他們的具體含義來判斷他們的用法,而不是死記硬背(哎,年紀大了,記性很差,只能經過理解來記住了。)redux

一個只知道安排任務的action

首先咱們先看action,相信這個單詞你們都認識。導演拍戲的時候,都喜歡說action!,這個既有行動的意思,也有開始的意思,在Redux中就是開始行動的意思。也就是說這個action是主動,而非被動的。導演說開始,你就要開始,並且還要按照劇本進行,一切都被安排地明明白白。因此,Redux的action,就是一個指示,告訴store,我要進行這個東西,你給安排下。有時候,咱們須要附帶點內容到store,才能進行下一步操做,這個時候就須要action攜帶規範的參數。數組

因此咱們怎麼寫action呢?包含兩點:bash

  • 註冊行爲
  • 註冊行爲所帶的參數

註冊行爲,安排行爲,標準每一個行爲的指令babel

const todo={
  TOGGLE_TODO:'TOGGLE_TODO',
  GET_TODOS:'GET_TODOS'
}
複製代碼

配置行爲所帶的參數,標準化參數,該帶上的都帶上,以避免被退回。其中type是行爲的類型,必須有,就像有些office要帶門卡同樣,忘了就再見!要被保安請出去的。後方的item就是自定義的,看看你的操做須要哪些參數,須要就帶上,不須要帶上的就不要帶上了,哪樣就很累贅了。異步

const todo={
  TOGGLE_TODO:'TOGGLE_TODO',
  GET_TODOS:'GET_TODOS',
  toggleTodo:function ({ items,id }) {
    return {
      type: todo.TOGGLE_TODO,
      items:items
      id:id
    }
  },
  getTodos: function getTodos({items}){
    return {
      type:todo.GET_TODOS,
      items:items
    }
  }
}
module.exports=todo
複製代碼

action只是告訴你發生了什麼,給你配齊了參數,可是不執行,不對傳入的對象(state)進行改變。*

可是既然action僅僅是配置而已,那麼怎麼操做呢,以後怎麼進行呢?Redux纔不會這麼好心幫咱們把後續操做給搞定了,這個時候咱們就要去研究Reducer了,一個我不知道如何翻譯的函數。

純純的reducer

如今咱們來看最沒法理解的reducer,爲何他要叫reducer,有什麼具體含義嗎?仍是隨便起的名字,總有一個緣由吧,就和爸媽給咱們取名字同樣老是寄予一些美好的期盼。

reducer的含義有不少,不少領域都用這個單詞,可是在Redux中,這應該是函數式語言(functional languages)中的一個概念。在wiki中,一般稱爲Fold,做爲一個結構轉變(structural transformations)的一個方式。

其餘語言Reducer實現方式咱們無論,咱們只看JS,在JS中出現的array.reduce(func,initval),相信你們應該很熟悉,也是一個剛上手就想哭的一個方法。數組中的reduce有什麼特色呢(不會用reduce的小夥伴,點擊這裏):

  • 給出了一個初始initval
  • 有一個轉換的func方法,這個方法必須是純函數,既不改變傳入對象的屬性
  • 最終返回一個新的對象/值,而array保持不變。

從而能夠推論出Redux中的reducer的特色:

  • 須要一個初始值,在redux中就是初始的state
  • 有一個轉換的方法,這個方法必須是純函數
  • 最終返回一個新的對象,也就是新的state,而傳入的state保持不變。

你們可能對純函數(pure funciton)有些模糊,函數還能pure??覺得是純牛奶呢??這裏的pure的意思就是他是乾乾淨淨的,毫無雜質。也就是一下兩點:

  • 不改變傳入的state的屬性,即不影響函數外的參數
  • 無反作用(side effect),好比觸發了某個事件,或者捕獲了什麼值。也就是與外界絕緣,我就守好個人二畝三分地就行了。

你們想深刻了解的話,雙手奉上wiki連接

那麼咱們該怎麼寫呢?

你們複習下上面的action,reducer但是action配套的好兄弟,一個在前,一個在後並肩做戰。

reducer並非主動執行,而是被動執行的,來了一個action,而後reducer處理下。

簡而言之:reducer就是響應來自action的召喚,而後返回一個新的對象(state)。

首先先導入來自action的行爲,這行爲是被註冊的,是能夠執行的。

const {TOGGLE_TODO,GET_TODOS}=require('../actions/Todo')
複製代碼

reducer的一個定義就是必定有個初始值,這裏設置一個也就是以防萬一。

const initialState={}
複製代碼

重點來了,reducer開始過濾註冊的行爲們,而後每次都是一個個配對,趕上配對的,就執行對應的代碼,而後返回一個新的state,若是輪了一邊發現這是未註冊的行爲,爲了防止這個行爲回去很差交代,就把原始的state給他帶回去交差。

module.exports=function(state = initialState, action) {
    switch(action.type){
        case GET_TODOS:
            return {todos:[...action.items]}
        case TOGGLE_TODO://deleted=true
            return {
                todos:action.items.map((i)=>{
                    if(i.id===action.id){
                        return {
                            ...i,
                            status:i.status===0?1:0
                        }
                    }else{
                        return {
                            ...i
                        } 
                    }
                })
            }
        default:
            return state;
    }
}
複製代碼

reducer根據action的指令,進行一系列操做,可是不改變原有的傳入對象(state),而是返回一個新的對象(state)。也就是Reducer,進行純計算,沒有異步,沒有污染,出來的值沒有意外,就像1+1必定等於2同樣,而後返回一個嶄新嶄新的對象(state)!

拜見老大store

雖action和reducer是一對基,配合默契,可是他們都是兩個獨立的模塊,怎麼醬他們連接起來,還須要一個橋樑。這個就是store,redux中真正的老大,你們都要經過store來執行下一步的人任務,以及要遵從store派發下來的任務。

咱們先配置一個老大吧,todoDemoList是假數據,你們懂的測試用。老大隻須要reducers這羣小弟來建幫派createStore。action再幫派創建之初並沒有用處。

const {createStore}= require('../../node_modules/redux')
const todo = require('./reducers/Todo')
const {toggleTodo,getTodos} = require('./actions/Todo')

let todoDemoList=(new Array(20)).join("|").split("|").map((v,i)=>{
    return {id:i,content:"demo"+(i+1),status:i%4===0?1:0}
})

const store=createStore(todo)
複製代碼

咱們的store究有何種能立呢?

  • 掌控整個應用的數據state,老大固然是掌控全局的啦,沒有什麼能瞞住他

  • 若是想要訪問應用數據state,那麼就要給store提申請,這個申請就是getState()

console.log(store.getState())//打印出來是一個空的{},固然啦!咱們都沒有給老大進貢,拿來的數據
複製代碼
  • 想要修改數據,那更要給老大提申請了,那就是dispatch(你的申請action)。action終於出場啦!
store.dispatch(getTodos({items:todoDemoList}))//給老大進貢
console.log(store.getState().todos[1])//有數據了
store.dispatch(toggleTodo({items:todoDemoList,id:1}))//修改某一個小羅羅
console.log(store.getState().todos[1])//修改爲功
複製代碼
  • 不要覺得你修改的時候能夠隨心所欲,老大看着呢。老大經過subscribe(監視器listener)看着你呢,小樣給我注意點。不過此刻要注意,咱們要把監聽器放在全部的dispatch執行以前,否則就捕捉不到dispatch了。要在一切還沒發生以前,就準備好,否則就要給小樣們鑽空子了。就像盜竊同樣,小偷已經偷完跑路了,你再裝監控,也逮不住這個小偷啊。
store.subscribe(()=>{
    console.log(store.getState())//監控這手下的一舉一動
})
複製代碼
  • 老大也不是不近人情之輩,若是說你的表現夠好,仍是會撤銷監視的。這個撤銷的方法就是store.subscribe()的返回值,這個返回值是一個方法,就像這樣store.subscribe()()
let unsubscribe=store.subscribe(()=>{
    console.log(store.getState())//監控這手下的一舉一動
})
store.dispatch(toggleTodo({items:todoDemoList,id:1}))//老大在watching,當心翼翼。
unsubscribe()//放了大家,出去浪吧
store.dispatch(toggleTodo({items:todoDemoList,id:1}))//竟然真的沒有再繼續監視我了,放飛自我。
複製代碼

老大怎麼可能只有一個小弟reducer呢?固然有不少啦!combineReducers(小弟們)

一個幫派只能有一個老大!可是能夠有頗有小弟,每一個小弟各司其職。可是要將這些小弟統一管理起來,這個時候就要用combineReducers了。

用相同的方法建立一個filterAction,而後用combineReducers一下,接下來就能夠愉快地合做了。

...
const filterAction = require('./reducers/filterAction')
const {showCompleted} = require('./actions/filterAction')
...
const rootReducer = combineReducers({todo: todo, filterAction: filterAction})
....
store.dispatch(showCompleted({items:todoDemoList}))//success執行啦!
複製代碼
相關文章
相關標籤/搜索