Redux探索之路

Redux簡介

Redux從設(shè)計之初就不是為了編寫最短递瑰、最快的代碼介时,他是為了解決 “當有確定的狀態(tài)發(fā)生改變時报咳,數(shù)據(jù)從哪里來” 這種可預(yù)測行為的問題的,所以當你使用Redux傳值時椎咧,代碼或許會很復(fù)雜玖详,這時候請你不要去懷疑是不是不該使用Redux。

Redux和Vuex一樣 是 JavaScript 狀態(tài)容器邑退,提供可預(yù)測化的狀態(tài)管理竹宋。實際學(xué)習(xí)起這兩種狀態(tài)管理器來,我自個感覺Redux相對難學(xué)一點地技,Redux不止可以用于React的狀態(tài)管理蜈七,它也可以用于Vue,只是當他用于React的時候,還必須依 React 綁定庫開發(fā)者工具莫矗。

npm install --save react-redux
npm install --save-dev redux-devtools

它和Vuex一樣有著store 理念,且action都不能直接修改state上面的狀態(tài)飒硅,Readux必須借助于reducers,所不同的是作谚,Vuex里面是由action發(fā)起三娩,然后再調(diào)用mutation修改(此處直接表明修改state上的何種屬性);Redux則直接由action修改妹懒,但必須由reducers去根據(jù)action.type去判斷修改的是state上面的哪個屬性雀监。

Redux也不支持多個Store,它只有一個單一的 store 和一個根級的 reduce 函數(shù)(reducer),隨著應(yīng)用不斷變大会前,你應(yīng)該把根級的 reducer 拆成多個小的 reducers好乐,分別獨立地操作 state 樹的不同部分,而不是添加新的 stores瓦宜。這就像一個 React 應(yīng)用只有一個根級的組件蔚万,這個根組件又由很多小組件構(gòu)成。

為什么要用狀態(tài)管理

當你一個state變化需要引起第一個view視圖層改變临庇,接著第一個的改變引起第二個反璃,以此類推,如果這個過程涉及到的組件很多假夺,那么交給你的任務(wù)將很龐大淮蜈,太繁瑣,往深了說如果這個過程是可逆的侄泽,那將是你的噩夢礁芦,而Redux的出現(xiàn)就是來解決這個問題的。

核心概念

state

可以把它理解成一個普通的對象悼尾,由鍵值對構(gòu)成柿扣,并且對鍵值對不做任何類型限制;
state是只讀的,唯一改變 state 的方法就是觸發(fā) action

{ key : value , key1 : value1 }

action

它也是一個普通的JS對象闺魏,只不過他有固定的鍵type
強制使用 action 來描述所有變化帶來的好處是可以清晰地知道應(yīng)用中到底發(fā)生了什么未状。如果一些東西改變了,就可以知道為什么變析桥。action 就像是描述發(fā)生了什么的指示器

{ type: 'ADD_TODO', text: 'Go to swimming pool' }
{ type: 'TOGGLE_TODO', index: 1 }
{ type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL' }

reducer

最終司草,為了把 action 和 state 串起來,開發(fā)一些函數(shù)泡仗,就需要用到 reducer埋虹。

//其一
 function userInfo (state = [],action){
    switch (action.type){
        case actionType.USERINFO_LOGIN:
            return action.data;
        case actionType.TENANTID:
            return action.data;
        case actionType.USERINFO_MENU:
            return action.data;
        default:
            return state;
    }
}
//其二
function visibilityFilter(state = 'SHOW_ALL', action) {
  if (action.type === 'SET_VISIBILITY_FILTER') {
    return action.filter;
  } else {
    return state;
  }
}
//匯總
 function todoApp(state = {}, action) {
  return {
    userInfo : userInfo (state.userInfo , action),
    visibilityFilter: visibilityFilter(state.visibilityFilter, action)
  };
}

另一種匯總的寫法

import { combineReducers, createStore } from 'redux'
let reducer = combineReducers({ visibilityFilter, userInfo })
let store = createStore(reducer)

純函數(shù):對于相同的輸入,永遠會得到相同的輸出娩怎,而且沒有任何可觀察的副作用搔课,也不會依賴外部環(huán)境的狀態(tài)。
注意 : 永遠不要在 reducer 里做這些操作:

  • 修改傳入?yún)?shù)截亦;
  • 執(zhí)行有副作用的操作爬泥,如 API 請求和路由跳轉(zhuǎn);
  • 調(diào)用非純函數(shù)崩瓤,如 Date.now() 或 Math.random()袍啡。

方法

bindActionCreators(actionCreators, dispatch)

把一個 value 為不同 action creator 的對象,轉(zhuǎn)成擁有同名 key 的對象却桶。同時使用 dispatch 對每個 action creator 進行包裝境输,以便可以直接調(diào)用它們。

一般情況下你可以直接在 Store 實例上調(diào)用 dispatch。如果你在 React 中使用 Redux嗅剖,react-redux 會提供 dispatch 函數(shù)讓你直接調(diào)用它 蛋逾。

惟一會使用到 bindActionCreators 的場景是當你需要把 action creator 往下傳到一個組件上,卻不想讓這個組件覺察到 Redux 的存在窗悯,而且不希望把 dispatch 或 Redux store 傳給它。

為方便起見偷拔,你也可以傳入一個函數(shù)作為第一個參數(shù)蒋院,它會返回一個函數(shù)。

參數(shù)

  1. actionCreators (Function or Object): 一個 action creator莲绰,或者一個 value 是 action creator 的對象欺旧。

  2. dispatch (Function): 一個由 Store 實例提供的 dispatch 函數(shù)。

返回值

(Function or Object): 一個與原對象類似的對象蛤签,只不過這個對象的 value 都是會直接 dispatch 原 action creator 返回的結(jié)果的函數(shù)辞友。如果傳入一個單獨的函數(shù)作為 actionCreators,那么返回的結(jié)果也是一個單獨的函數(shù)震肮。

combineReducers(reducers)

combineReducers 輔助函數(shù)的作用是称龙,把一個由多個不同 reducer 函數(shù)作為 value 的 object,合并成一個最終的 reducer 函數(shù)戳晌,然后就可以對這個 reducer 調(diào)用 createStore 方法鲫尊。

技巧

{ ...state, visibilityFilter: action.filter }

  • 縮減樣板代碼

1.將每個 action type 定義為 string 常量:const ADD_TODO = 'ADD_TODO';const REMOVE_TODO = 'REMOVE_TODO';
這樣做的優(yōu)勢是:

  • 幫助維護命名一致性,因為所有的 action type 匯總在同一位置沦偎。
  • 有時疫向,在開發(fā)一個新功能之前你想看到所有現(xiàn)存的 actions 。而你的團隊里可能已經(jīng)有人添加了你所需要的action豪嚎,而你并不知道搔驼。
  • Action types 列表在 Pull Request 中能查到所有添加,刪除侈询,修改的記錄舌涨。這能幫助團隊中的所有人及時追蹤新功能的范圍與實現(xiàn)。
  • 如果你在 import 一個 Action 常量的時候拼寫錯了妄荔,你會得到 undefined 泼菌。在 dispatch 這個 action 的時候,Redux 會立即拋出這個錯誤啦租,你也會馬上發(fā)現(xiàn)錯誤哗伯。

2.通過創(chuàng)建函數(shù)生成 action 對象

export function addTodo(text) {
  return {
    type: 'ADD_TODO',
    text
  }
}

export function editTodo(id, text) {
  return {
    type: 'EDIT_TODO',
    id,
    text
  }
}

也可以用于生成 action creator 的函數(shù)

function makeActionCreator(type, ...argNames) {
  return function(...args) {
    let action = { type }
    argNames.forEach((arg, index) => {
      action[argNames[index]] = args[index]
    })
    return action
  }
}
const ADD_TODO = 'ADD_TODO'
export const addTodo = makeActionCreator(ADD_TODO, 'todo')

問題

Redux 只能搭配 React 使用?

不是篷角,他可以Angular焊刹、 Angular 2、 Vue、 Mithril 等框架使用虐块。

Redux 需要特殊的編譯工具支持嗎俩滥?

Redux 寫法遵循 ES6 語法,但在發(fā)布時被 Webpack 和 Babel 編譯成了 ES5贺奠,所以在使用時可以忽略 JavaScript 的編譯過程霜旧。

將怎樣的數(shù)據(jù)放入 Redux 的經(jīng)驗法則:

  • 應(yīng)用的其他部分是否關(guān)心這個數(shù)據(jù)?
  • 是否需要根據(jù)需要在原始數(shù)據(jù)的基礎(chǔ)上創(chuàng)建衍生數(shù)據(jù)儡率?
  • 相同的數(shù)據(jù)是否被用作驅(qū)動多個組件挂据?
  • 能否將狀態(tài)恢復(fù)到特定時間點(在時光旅行調(diào)試的時候)?
  • 是否要緩存數(shù)據(jù)(比如:數(shù)據(jù)存在的情況下直接去使用它而不是重復(fù)去請求他)儿普?

如何在 reducer 之間共享 state? combineReducers 是必須的嗎崎逃?

要共享state,建議將state再次拆分成小模塊眉孩;
combineReducers 不是 必須的个绍,它僅僅是通過簡單的 JavaScript 對象作為數(shù)據(jù),讓 state 層能與 reducer 一一關(guān)聯(lián)的函數(shù)而已浪汪。

如何組織State

設(shè)計規(guī)范化的State
范式化的數(shù)據(jù)包含下面幾個概念:

  • 任何類型的數(shù)據(jù)在 state 中都有自己的 “表”巴柿。
  • 任何 “數(shù)據(jù)表” 應(yīng)將各個項目存儲在對象中,其中每個項目的 ID 作為 key吟宦,項目本身作為 value篮洁。
  • 對單個項目的引用都應(yīng)該根據(jù)存儲項目的 ID 來完成。
  • ID 數(shù)組應(yīng)該用于排序

一個項目允許出現(xiàn)多個store嗎殃姓?

Flux 原始模型中一個應(yīng)用有多個 “store”袁波,每個都維護了不同維度的數(shù)據(jù)。這樣導(dǎo)致了類似于一個 store “等待” 另一 store 操作的問題蜗侈。Redux 項目中只有一個store篷牌,而且通過將 reducer 分解成多個小而美的 reducer,進而切分數(shù)據(jù)域踏幻,隱含的實現(xiàn)了多個store枷颊,但又避免了上述情況的發(fā)生。

為何 type 必須是字符串该面,或者至少可以被序列化夭苗? 為什么 action 類型應(yīng)該作為常量?

無法強制序列化 action隔缀,所以 Redux 只會校驗 action 是否是普通對象题造,以及 type 是否定義。其它的都交由你決定猾瘸,但是確保數(shù)據(jù)是可序列化將對調(diào)試以及問題的重現(xiàn)有很大幫助
雖然可以在任何地方手動創(chuàng)建 action 對象界赔、手動指定 type 值丢习,定義常量的方式使得代碼的維護更為方便。

是否存在 reducer 和 action 之間的一對一映射淮悼?

不存在咐低。建議的方式是編寫?yīng)毩⑶液苄〉?reducer 方法去更新指定的 state 部分,這種模式被稱為 “reducer 合成”袜腥。一個指定的 action 也許被它們中的全部见擦、部分、甚至沒有一個處理到羹令。這種方式把組件從實際的數(shù)據(jù)變更中解耦锡宋,一個 action 可能影響到 state 樹的不同部分,對組件而言再也不必知道這些了特恬。有些用戶選擇將它們緊密綁定在一起,就像 “ducks” 文件結(jié)構(gòu)徐钠,顯然是沒有默認的一對一映射癌刽。所以當你想在多個 reducer 中處理同一個 action 時,應(yīng)當避免此類結(jié)構(gòu)尝丐。

為何組件沒有被重新渲染显拜、或者 mapStateToProps 沒有運行?

如果直接返回同一對象爹袁,即使你改變了數(shù)據(jù)內(nèi)容远荠,Redux 也會認為沒有變化。

為何不在被連接的組件中使用 this.props.dispatch失息?

connect() 方法有兩個主要的參數(shù)譬淳,而且都是可選的。第一個參數(shù) mapStateToProps 是個函數(shù)盹兢,讓你在數(shù)據(jù)變化時從 store 獲取數(shù)據(jù)邻梆,并作為 props 傳到組件中。第二個參數(shù) mapDispatchToProps 依然是函數(shù)绎秒,讓你可以使用 store 的 dispatch 方法浦妄,通常都是創(chuàng)建 action 創(chuàng)建函數(shù)并預(yù)先綁定,那么在調(diào)用時就能直接分發(fā) action见芹。

如果在執(zhí)行 connect() 時沒有指定 mapDispatchToProps 方法剂娄,React Redux 默認將 dispatch 作為 prop 傳入。所以當你指定方法時玄呛, dispatch 將 不 會自動注入阅懦。如果你還想讓其作為 prop,需要在 mapDispatchToProps 實現(xiàn)的返回值中明確指出把鉴。

store 里能直接通過 store.dispatch() 調(diào)用 dispatch() 方法故黑,但是多數(shù)情況下你會使用 react-redux 提供的 connect() 幫助器來調(diào)用儿咱。bindActionCreators() 可以自動把多個 action 創(chuàng)建函數(shù) 綁定到 dispatch() 方法上。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末场晶,一起剝皮案震驚了整個濱河市混埠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌诗轻,老刑警劉巖钳宪,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異扳炬,居然都是意外死亡吏颖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門恨樟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來半醉,“玉大人,你說我怎么就攤上這事劝术∷醵啵” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵养晋,是天一觀的道長衬吆。 經(jīng)常有香客問我,道長绳泉,這世上最難降的妖魔是什么逊抡? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮零酪,結(jié)果婚禮上冒嫡,老公的妹妹穿的比我還像新娘。我一直安慰自己四苇,他們只是感情好灯谣,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蛔琅,像睡著了一般胎许。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上罗售,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天辜窑,我揣著相機與錄音,去河邊找鬼寨躁。 笑死穆碎,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的职恳。 我是一名探鬼主播所禀,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼方面,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了色徘?” 一聲冷哼從身側(cè)響起恭金,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎褂策,沒想到半個月后横腿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡斤寂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年耿焊,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片遍搞。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡罗侯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出溪猿,到底是詐尸還是另有隱情歇父,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布再愈,位于F島的核電站,受9級特大地震影響护戳,放射性物質(zhì)發(fā)生泄漏翎冲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一媳荒、第九天 我趴在偏房一處隱蔽的房頂上張望抗悍。 院中可真熱鬧,春花似錦钳枕、人聲如沸缴渊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衔沼。三九已至,卻和暖如春昔瞧,著一層夾襖步出監(jiān)牢的瞬間指蚁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工自晰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留凝化,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓酬荞,卻偏偏與公主長得像搓劫,于是被迫代替她去往敵國和親瞧哟。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容