摘要:只要輸入的值不變,每次輸出都是一樣的值。指定位置元素運(yùn)算操作如可用以下方式代替主要是生成中最核心的對(duì)象。描述發(fā)生了什么,是響應(yīng)并對(duì)進(jìn)行更新。生成的對(duì)象包含個(gè)方法,分別為,和。按照約定,具有字段來(lái)表示它的類型。
前言:redux基礎(chǔ)用法 1. Action
一開(kāi)始接觸redux的時(shí)候最令我記住的一句話是:You Might Not Need Redux(那我還寫這篇文章干嘛?手動(dòng)滑稽)
回歸正題,本文主要是圍繞redux的作者Dan的視頻,由淺入深了解redux
如果需要改變state(狀態(tài)),我們需要使用到Action。Action是一個(gè)普通的JavaScript對(duì)象描述state變化。action的屬性可以自定義,但是必須有一個(gè)叫type的屬性,值是string類型(方便序列化)。每一個(gè)action都是對(duì)state的一個(gè)(minimal change)最小修改,在應(yīng)用里什么東西發(fā)生了變化。
//創(chuàng)建一個(gè)加法器,減法器 const INCREMENT = "INCREMENT"; { type: INCREMENT } const DECREMENT = "DECREMENT"; { type: DECREMENT } //----------------------------- //比如添加新todo任務(wù) // action type字符常量 const ADD_TODO = "ADD_TODO"; // ADD_TODO action { type: ADD_TODO, text: "Learn Redux" }2. 純函數(shù)和非純函數(shù)
什么是純函數(shù)?
純函數(shù)就是沒(méi)有副作用的函數(shù),不包含數(shù)據(jù)庫(kù)查詢、網(wǎng)絡(luò)請(qǐng)求等操作。只要輸入的值不變,每次輸出都是一樣的值。
//pure function function square(x) { return x * x; } // Impure function function square(x) { updateXInDatabase(x); return x * x; }
為什么需要說(shuō)純函數(shù)?
因?yàn)樵趓edux中,有些函數(shù)必須是純函數(shù),比如reducer,所以在編碼的時(shí)候有必要注意。
注意!reducer必須是純函數(shù),不能更改state,每次都必須返回一個(gè)新的state。
reducer,其實(shí)就是描述舊狀態(tài)(previous state)如何轉(zhuǎn)換為當(dāng)前狀態(tài)(current state)的函數(shù)。
這里使用的是計(jì)數(shù)器例子,我們通過(guò)action的type
const counter = (state = 0, action) => { switch (action.type) { case "INCREMENT": return state + 1; case "DECREMENT": return state - 1; default: return state; } }
reducer是需要固定的形式的,需要傳入2個(gè)參數(shù),一個(gè)是state,另一個(gè)是action。
我們這里先不要管為什么要這么寫,記住這是redux的規(guī)定即可。我們可以通過(guò)es6的默認(rèn)參數(shù)方式賦予state初始默認(rèn)值。
因?yàn)閞educer是純函數(shù),因此需要注意幾點(diǎn):
不能改變參數(shù)
不能使用非純函數(shù)
接下來(lái)我們一起看一下怎么處理reducer中對(duì)數(shù)據(jù)的處理。
reducer的state處理通常是兩種數(shù)據(jù)類型:
Object
Array
Object// 注意,不能直接改變state中的屬性值 state.name = "ken" // ?這種對(duì)name屬性賦值操作是不允許的 // --------------------------- // 正確??做法 // 1,使用Object的assign方法,需要對(duì)瀏覽器做polyfill return Object.assign({}, state, { name: "ken" }); // 或者使用es7的解構(gòu)方法 return { ...state, name: "ken", };Array
array的操作中,常用操作為:
增(push)
刪(splice)
指定位置元素運(yùn)算操作,如 array[index]++ array[index] = array[index] * 2等操作
注意上述3個(gè)操作都是會(huì)改變?cè)瓟?shù)組,因此需要使用“純”操作代替這些“非純”操作。
push/pop/shift/unshift
如果需要使用push,我們可以使用concat方法代替。
concat方法返回一個(gè)新數(shù)組,且不改變?cè)瓟?shù)組。
array.push(x); // ? array.concat(x); // ? // 或者使用解構(gòu)方式 [...array, x];
splice
同理,splice操作也是會(huì)改變?cè)瓟?shù)組,我們可以用slice去代替。
array.splice(index, 1); // ? [...array.slice(0, index), ...array.slice(index + 1)]; // ?
指定位置元素運(yùn)算操作
// 如 array[index]++ // ? // 可用以下方式代替 ? [...array.slice(0, index), array[index] + 1, ...array.slice(index + 1)];5. createStore
createStore主要是生成redux中最核心的Store對(duì)象。action描述“發(fā)生了什么”,reducer是響應(yīng)action并對(duì)state進(jìn)行更新。而store可以看成把action和reducer聯(lián)系起來(lái)的一個(gè)對(duì)象。
import { createStore } from redux; const store = createStore(counter) console.log(store.getState()); // output 0 store.dispatch({ type: "INCREMENT"} ); console.log(store.getState()); // output 1 // ------------------------------ const render = () => { document.body.innerText = store.getState(); }; store.subscribe(render); document.addEventListener("click", () => { store.dispatch({ type: "INCREMENT" }); });
createStore生成的store對(duì)象包含3個(gè)方法,分別為getState,dispatch和subscribe。其中subscribe函數(shù)的返回值是一個(gè)函數(shù),用于取消訂閱。
getState()返回應(yīng)用當(dāng)前的state樹(shù)。
dispatch(action)分發(fā)action。這個(gè)是觸發(fā)state改變的唯一方法。
action是描述應(yīng)用變化的普通對(duì)象。按照約定,action 具有 type 字段來(lái)表示它的類型。type 也可被定義為常量或者是從其它模塊引入。
subscribe(listener)添加一個(gè)變化監(jiān)聽(tīng)器。listener是一個(gè)函數(shù),每當(dāng)執(zhí)行dispatch方法時(shí)候就會(huì)執(zhí)行l(wèi)istener。 在例子中,每次dispatch一個(gè)action,就會(huì)觸發(fā)render函數(shù)執(zhí)行。如果需要解除綁定監(jiān)聽(tīng),執(zhí)行subscribe返回的函數(shù)即可。
6. 實(shí)現(xiàn)一個(gè)簡(jiǎn)單版的createStore我們知道createStore返回的是一個(gè)store對(duì)象,其中包括getState,dispatch和subscribe方法。
當(dāng)然接下來(lái)的實(shí)現(xiàn)是最簡(jiǎn)單的實(shí)現(xiàn),去掉了很多參數(shù)校驗(yàn)、store enhancer(即中間件)和類RxJS等reactive庫(kù)支持。有興趣更深了解的同學(xué)可以看看redux的createStore源碼(ps:我打算下一篇文章寫關(guān)于redux源碼??)
const CreateStore = (reducers, initState = {}) { let currentState = initState; let listeners = []; // getState就是簡(jiǎn)單的把當(dāng)前的state返回給調(diào)用者 const getState = () => currrentState; // subscribe: // 如果對(duì)觀察者模式(發(fā)布-訂閱模式)比較熟悉的同學(xué)就會(huì)發(fā)現(xiàn),這其實(shí)就是做訂閱 // const subscribe = listener => { listeners.push(listener); return function unsubscribe() { listeners = listeners.filter(currentListener => currentListener !== listener) }; }; // 同理,這個(gè)是觀察者模式中的發(fā)布 // 接收到action后,通過(guò)reducer更新state,并且把listeners執(zhí)行一遍 const dispatch = action => { reducers(currentState, action); listens.forEach(listener => listener()); } // 初始化 // 讓reducers返回他們的初始state dispatch({}); return { getState, subscribe, dispatch, }; };
我們很簡(jiǎn)單就實(shí)現(xiàn)了createStore,當(dāng)然實(shí)際上還是會(huì)稍微復(fù)雜一點(diǎn)。
7. combineReducers從上述我們實(shí)現(xiàn)的createStore源碼可以看出,傳入的reducer只有一個(gè)。
真實(shí)的應(yīng)用中reducer是多個(gè)的,因此我們需要把reducer組合起來(lái)。combineReducers就是幫我們完成這個(gè)任務(wù)。
import { combineReducers } from "redux"; const todosReducer = (state = {}, action) => { switch (action.type) { ... } }; const visibilityFilterReducer = (state = {}, action) => { switch (action.type) { ... } }; const reducer = combineReducers({ todos: todosReducer, visibilityFilter: visibilityFilterReducer, }); const store = createStore(reducer, {});
代碼主要完成了把todosReducer和visibilityFilterReducer合并為一個(gè)reducer;todos和visibilityFilter分別是兩個(gè)變量接收兩個(gè)reducer處理的結(jié)果。
其實(shí)我們可以這么理解
const todosAndVisibilityFilterReducer = (state = { todos: {}, visibilityFilter: {} }, action) => { switch (action.type) { ... // 合并todosReducer和visibilityFilterReducer的case即可 } } const store = createStore(todosAndVisibilityFilterReducer, {});
當(dāng)然真實(shí)項(xiàng)目不能這么搞,既然redux提供了combineReducers,我們就應(yīng)該使用。
8. 實(shí)現(xiàn)一個(gè)簡(jiǎn)單版的combineReducers和createStore一樣,我們寫一個(gè)簡(jiǎn)單版本的combineReducers。
//可以理解combineReducers就是一個(gè)reducer,因此其返回值是一個(gè)可以傳入(state, action)的函數(shù) const combineReducers = (reducers = {}) => { return (state = {}, action) => { // 和普通的reducer一樣,返回一個(gè)新的state作為返回值 // 此處選擇Array.reducer方法更加簡(jiǎn)潔;forEach還要?jiǎng)?chuàng)建一個(gè)臨時(shí)變量保存新值。 return Object.keys(reducers).reduce((nextState, key) => { // 這里的key可以套入todos/visibilityFilter // 執(zhí)行每個(gè)reducer方法,并取得其返回的state值,放入nextState中 nextState[key] = reducers[key](state[key], action); return nextState; }); } };參考
redux官網(wǎng)
Dan的Getting Started With Redux系列課程(非常贊,5星級(jí)推薦)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/98720.html
摘要:資料合集學(xué)習(xí)有段時(shí)間了,相關(guān)不錯(cuò)的資料整理下,希望能幫到有緣人五顆星推薦中文文檔通讀不下邊,翻譯的很好,想理解清楚,定下心來(lái),認(rèn)真讀,必有收獲官方推薦資料合集,沒(méi)有啥說(shuō)的中間件深入淺出就因?yàn)榭戳诉@篇文章才更加深入學(xué)習(xí),同時(shí)對(duì)有了別樣的看法 redux 資料合集 學(xué)習(xí)redux有段時(shí)間了,相關(guān)不錯(cuò)的資料整理下,希望能幫到有緣人 五顆星推薦 中文文檔 通讀不下3邊,翻譯的很好,想理解清楚...
摘要:另外,內(nèi)置的函數(shù)在經(jīng)過(guò)一系列校驗(yàn)后,觸發(fā),之后被更改,之后依次調(diào)用監(jiān)聽(tīng),完成整個(gè)狀態(tài)樹(shù)的更新??偠灾?,遵守這套規(guī)范并不是強(qiáng)制性的,但是項(xiàng)目一旦稍微復(fù)雜一些,這樣做的好處就可以充分彰顯出來(lái)。 這一篇是接上一篇react進(jìn)階漫談的第二篇,這一篇主要分析redux的思想和應(yīng)用,同樣參考了網(wǎng)絡(luò)上的大量資料,但代碼同樣都是自己嘗試實(shí)踐所得,在這里分享出來(lái),僅供一起學(xué)習(xí)(上一篇地址:個(gè)人博客/s...
摘要:另外,內(nèi)置的函數(shù)在經(jīng)過(guò)一系列校驗(yàn)后,觸發(fā),之后被更改,之后依次調(diào)用監(jiān)聽(tīng),完成整個(gè)狀態(tài)樹(shù)的更新。總而言之,遵守這套規(guī)范并不是強(qiáng)制性的,但是項(xiàng)目一旦稍微復(fù)雜一些,這樣做的好處就可以充分彰顯出來(lái)。 這一篇是接上一篇react進(jìn)階漫談的第二篇,這一篇主要分析redux的思想和應(yīng)用,同樣參考了網(wǎng)絡(luò)上的大量資料,但代碼同樣都是自己嘗試實(shí)踐所得,在這里分享出來(lái),僅供一起學(xué)習(xí)(上一篇地址:個(gè)人博客/s...
摘要:上一篇文章講解了如何使用,本篇文章將進(jìn)一步深入,從的源碼入手,深入學(xué)習(xí)的中間件機(jī)制。的功能是讓支持異步,讓我們可以在中跟服務(wù)器進(jìn)行交互等操作,而他的實(shí)現(xiàn)。。。 上一篇文章講解了redux如何使用,本篇文章將進(jìn)一步深入,從redux的源碼入手,深入學(xué)習(xí)redux的中間件機(jī)制。在這里我們會(huì)以一個(gè)redux-thunk中間件為例,逐步分解redux的中間機(jī)制如何操作,如何執(zhí)行。 閑話不多說(shuō),...
摘要:謹(jǐn)記,請(qǐng)勿犯這樣的錯(cuò)誤。由于在之前的教程中,積累了堅(jiān)實(shí)的基礎(chǔ)。其實(shí),這是有緣由的其復(fù)雜度在早期的學(xué)習(xí)過(guò)程中,將會(huì)帶來(lái)災(zāi)難性的影響。該如何應(yīng)對(duì)對(duì)于來(lái)說(shuō),雖然有大量的學(xué)習(xí)計(jì)劃需要采取,且有大量的東西需要學(xué)習(xí)。 前言倘若你正在建造一間房子,那么為了能快點(diǎn)完成,你是否會(huì)跳過(guò)建造過(guò)程中的部分步驟?如在具體建設(shè)前先鋪設(shè)好部分石頭?或直接在一塊裸露的土地上先建立起墻面? 又假如你是在堆砌一個(gè)結(jié)婚蛋糕...
閱讀 1661·2021-11-22 14:45
閱讀 1112·2021-11-17 09:33
閱讀 3361·2021-09-02 09:48
閱讀 997·2019-08-30 15:54
閱讀 2793·2019-08-30 15:53
閱讀 2582·2019-08-30 12:54
閱讀 2272·2019-08-29 12:37
閱讀 2449·2019-08-26 13:58