Redux的基本使用過程步驟詳解
Redux的使用過程
Redux測試項(xiàng)目的搭建
1.創(chuàng)建一個(gè)新的項(xiàng)目文件夾:learn-redux
# 執(zhí)行初始化操作
npm init -y
或yarn init -y
# 安裝redux:
npm install redux --save
或yarn add redux
2.創(chuàng)建src目錄,在src目錄下創(chuàng)建一個(gè)store文件夾, 并且在該文件夾下創(chuàng)建index.js文件
3.可以修改package.json用于執(zhí)行index.js, 也可以不配置, 直接使用node命令運(yùn)行
"scripts": { "start": "node src/index.js" }
Redux的基本使用步驟
1.創(chuàng)建一個(gè)對(duì)象,作為我們要保存的狀態(tài)state:
// 由于測試項(xiàng)目在node環(huán)境下, 因此使用require方式導(dǎo)入 const { createStore } = require("redux") // 創(chuàng)建的要存儲(chǔ)的state: initialState const initialState = { name: "chenyq", age: 18 }
2.創(chuàng)建Store來存儲(chǔ)這個(gè)state
由于創(chuàng)建的state是不能直接放到創(chuàng)建的store中的, 需要通過reducer將數(shù)據(jù)添加到store中, 因此創(chuàng)建store時(shí)必須創(chuàng)建reducer;
reducer函數(shù)的返回值, 會(huì)作為store之后存儲(chǔ)的state
// 定義reducer, 將要存儲(chǔ)的state作為返回值返回 function reducer() { return initialState } // 創(chuàng)建的store, 內(nèi)部會(huì)自動(dòng)回調(diào)reducer, 拿到initialState const store = createStore(reducer) // 導(dǎo)出store module.exports = store
我們可以在其他文件中通過
store.getState
來獲取當(dāng)前的state;
// 導(dǎo)入創(chuàng)建的store const store = require("./store") // 獲取store中的state console.log(store.getState())
3.通過action來修改state
錯(cuò)誤演示
: 直接修改store
store.getState().name = "abc"
修改store中的數(shù)據(jù)不能直接修改, 必須要通過dispatch來派發(fā)action;
通常action中都會(huì)有type屬性,也可以攜帶其他的數(shù)據(jù);
// 定義一個(gè)action const nameAction = { type: "change_name", name: "abc" } // 派發(fā)action store.dispatch(nameAction)
當(dāng)然上面代碼中, 也可以寫為一行
// 派發(fā)action store.dispatch({ type: "change_name", name: "abc" })
4.修改reducer中的處理代碼
reducer接收兩個(gè)參數(shù):
參數(shù)一: store中當(dāng)前保存的state
參數(shù)二: 本次需要更新的action
只要調(diào)用dispatch就會(huì)重新執(zhí)行reducer函數(shù)
這里一定要記住,reducer是一個(gè)純函數(shù),不可以直接修改state, 后面我會(huì)講到直接修改state帶來的問題;
// 第一次state是undefined, 因此給一個(gè)默認(rèn)值將初始化數(shù)據(jù)添加到store中 function reducer(state = initialState, action) { // 有數(shù)據(jù)更新時(shí), 返回一個(gè)新的state if (action.type === "change_name") { return { ...state, name: action.name } } // 沒有數(shù)據(jù)更新時(shí), 返回之前的state return state }
5.可以在派發(fā)action之前,監(jiān)聽store的變化:
通過
store.subscribe()
函數(shù)可以監(jiān)聽store中的數(shù)據(jù)變化
store.subscribe()
函數(shù)的參數(shù)接收一個(gè)函數(shù), 該函數(shù)在store數(shù)據(jù)發(fā)生更新自動(dòng)回調(diào)
const store = require("./store") // 例如: 監(jiān)聽數(shù)據(jù)的變化, 當(dāng)store變化, 就獲取最新的state store.subscribe(() => { console.log(store.getState()) }) store.dispatch({ type: "change_name", name: "abc" }) store.dispatch({ type: "change_name", name: "aaa" })
6.封裝函數(shù)動(dòng)態(tài)生成action
例如上面代碼中, 我們修改名稱多次, 只有傳入的action的name屬性值不相同, 那么我們可以封裝一個(gè)函數(shù), 動(dòng)態(tài)的生成action, 這也是開發(fā)中一貫的做法
// 創(chuàng)建修改name的action const changeNameAction = (name) => ({ type: "change_name", name }) // 創(chuàng)建修改age的action const changeAgeAction = (num) => ({ type: "change_age", num }) // 在派發(fā)action時(shí), 我們就可以調(diào)用函數(shù)即可獲取action store.dispatch(changeNameAction("aaa")) store.dispatch(changeNameAction("bbb")) store.dispatch(changeNameAction("ccc")) store.dispatch(changeAgeAction(10)) store.dispatch(changeAgeAction(20)) store.dispatch(changeAgeAction(30)) store.dispatch(changeAgeAction(40))
Redux目錄的結(jié)構(gòu)劃分
如果我們將所有的邏輯代碼寫到一起,那么當(dāng)redux變得復(fù)雜時(shí)代碼就難以維護(hù)。
例如上面代碼中, 我們封裝的動(dòng)態(tài)創(chuàng)建action的函數(shù), 這種動(dòng)態(tài)生成action的函數(shù)在項(xiàng)目中可能會(huì)有很多個(gè), 而且在其他多個(gè)文件中也可能會(huì)使用, 所以我們最好是有一個(gè)單獨(dú)的文件夾存放這些動(dòng)態(tài)獲取action的函數(shù)
接下來,我會(huì)對(duì)代碼進(jìn)行拆分,將store、reducer、action、constants拆分成一個(gè)個(gè)文件。
創(chuàng)建store/index.js文件: index文件中, 我們只需要?jiǎng)?chuàng)建store即可
const { createStore } = require("redux") // 引入reducer const reducer = require("./reducer") // 創(chuàng)建的store, 內(nèi)部會(huì)自動(dòng)回調(diào)reducer, 拿到initialState const store = createStore(reducer) // 導(dǎo)出store module.exports = store
創(chuàng)建store/reducer.js文件: 在真實(shí)項(xiàng)目中, reducer這個(gè)函數(shù)我們會(huì)越寫越復(fù)雜, 造成我們index.js文件越來越大, 所以我們將reducer也抽離到一個(gè)單獨(dú)的文件中
const { CHANGE_NAME, CHANGE_AGE } = require("./constants") // 創(chuàng)建的要存儲(chǔ)的初始化state const initialState = { name: "chenyq", age: 18 } // 定義reducer, 將要存儲(chǔ)的state作為返回值返回 // 第一次state是undefined, 因此給一個(gè)默認(rèn)值將初始化數(shù)據(jù)添加到store中 function reducer(state = initialState, action) { switch(action.type) { case CHANGE_NAME: return { ...state, name: action.name } case CHANGE_AGE: return {...state, age: state.age + action.num} } // 沒有數(shù)據(jù)更新時(shí), 返回之前的state return state } module.exports = reducer
創(chuàng)建store/constants.js文件: 將type的類型定義為常量(防止寫錯(cuò)的情況), 這些常量最好也防止一個(gè)單獨(dú)的文件中
// store/constants.js const CHANGE_NAME = "change_name" const CHANGE_AGE = "change_age" module.exports = { CHANGE_NAME, CHANGE_AGE }
創(chuàng)建store/actionCreators.js文件: 將封裝的動(dòng)態(tài)創(chuàng)建action的函數(shù)放在該文件中, 在需要使用的地方導(dǎo)入即可
const { CHANGE_NAME, CHANGE_AGE } = require("./store/constants") // 創(chuàng)建修改name的action const changeNameAction = (name) => ({ type: CHANGE_NAME, name }) // 創(chuàng)建修改age的action const changeAgeAction = (num) => ({ type: CHANGE_AGE, num }) module.exports = { changeNameAction, changeAgeAction }
最終形成如下目錄結(jié)構(gòu), 這也是官方推薦的目錄結(jié)構(gòu), 一個(gè)store中包含這四個(gè)文件夾
注意:node中對(duì)ES6模塊化的支持, 建議使用CommonJS規(guī)范
React的三大原則
單一數(shù)據(jù)源
整個(gè)應(yīng)用程序的state被存儲(chǔ)在一顆object tree中,并且這個(gè)object tree只存儲(chǔ)在一個(gè) store 中:
Redux并沒有強(qiáng)制讓我們不能創(chuàng)建多個(gè)Store,但是那樣做并不利于數(shù)據(jù)的維護(hù);
單一的數(shù)據(jù)源可以讓整個(gè)應(yīng)用程序的state變得方便維護(hù)、追蹤、修改;
State是只讀的
唯一修改State的方法一定是觸發(fā)action,不要試圖在其他地方通過任何的方式來修改State:
這樣就確保了View或網(wǎng)絡(luò)請(qǐng)求都不能直接修改state,它們只能通過action來描述自己想要如何修改state;
這樣可以保證所有的修改都被集中化處理,并且按照嚴(yán)格的順序來執(zhí)行,所以不需要擔(dān)心race condition(竟態(tài))的問題;
使用純函數(shù)來執(zhí)行修改
通過reducer將舊state和actions聯(lián)系在一起,并且返回一個(gè)新的State:
隨著應(yīng)用程序的復(fù)雜度增加,我們可以將reducer拆分成多個(gè)小的reducers,分別操作不同state tree的一部分;
但是所有的reducer都應(yīng)該是純函數(shù),不能產(chǎn)生任何的副作用;
到此這篇關(guān)于Redux的基本使用過程詳解的文章就介紹到這了,更多相關(guān)Redux基本使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript字符串循環(huán)匹配實(shí)例分析
這篇文章主要介紹了javascript字符串循環(huán)匹配,實(shí)例分析三種常用的字符串循環(huán)匹配的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07javascript發(fā)送短信驗(yàn)證碼實(shí)現(xiàn)代碼
我們?cè)谧?cè)賬號(hào),或者是參加活動(dòng)時(shí),都會(huì)向手機(jī)發(fā)送收短信驗(yàn)證碼,短信驗(yàn)證碼到底是如何實(shí)現(xiàn)的,本文為大家揭曉,并為大家分項(xiàng)1javascript發(fā)送短信驗(yàn)證碼實(shí)現(xiàn)代碼,感興趣的小伙伴們可以參考一下2015-11-11JavaScript實(shí)現(xiàn)標(biāo)題欄文字輪播效果代碼
這篇文章主要介紹了JavaScript實(shí)現(xiàn)標(biāo)題欄文字輪播效果代碼,涉及JavaScript基于時(shí)間函數(shù)及流程控制操作標(biāo)題欄文字的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10JavaScript常用數(shù)組操作方法,包含ES6方法
這篇文章主要介紹了JavaScript常用數(shù)組操作方法,包含ES6方法,本文分步驟給大家介紹的非常詳細(xì),具有一定的參考借鑒借鑒價(jià)值,需要的朋友可以參考下2018-09-09javascript實(shí)現(xiàn)的登陸遮罩效果匯總
小編給大家推薦幾款使用Javascript實(shí)現(xiàn)的遮罩效果登陸框,其實(shí)這種效果是很常見的,在許多互動(dòng)的社區(qū)及其它的一些地方,彈出框應(yīng)用想當(dāng)流行,在不妨礙網(wǎng)頁運(yùn)行的情況下,用戶可以輸入登錄信息,實(shí)現(xiàn)完美登錄。2015-11-11JavaScript擴(kuò)展運(yùn)算符的學(xué)習(xí)及應(yīng)用詳情(ES6)
這篇文章主要介紹了JavaScript擴(kuò)展運(yùn)算符的學(xué)習(xí)及應(yīng)用詳情(ES6),擴(kuò)展運(yùn)算符是ES6新增的一種運(yùn)算符,他可以幫助我們簡化代碼,簡化操作,具體相關(guān)知識(shí)感興趣的小伙伴可以查看下面文章的簡單介紹2022-08-08