詳解Redux的工作流程

(當(dāng)然不是這張圖)
Redux理解
redux是什么
- redux是一個(gè)專門用于做狀態(tài)管理的JS庫(kù)(不是react插件庫(kù));
- 它可以在react、angular、vue等項(xiàng)目中,但基本與react配合使用;
- 作用:集中式管理react應(yīng)用中多個(gè)組件共享的狀態(tài)。
什么情況下需要使用redux?
- 某個(gè)組件的狀態(tài),需要讓其他組件可以隨時(shí)拿到(共享);
- 一個(gè)組件需要改變另一個(gè)組件的狀態(tài)(通信);
- 總體原則:能不用就不用,如果不用比較吃力才考慮使用。
redux工作流程??圖

action
- 動(dòng)作的對(duì)象
- 包含兩個(gè)屬性:
- type:標(biāo)識(shí)屬性,值為字符串,唯一,必要屬性;
- data:數(shù)據(jù)屬性,值類型任意,可選屬性。
- 例子:{ type:‘ADD_STUDENT’, data:{name: ‘tom’,age: 18} }
reducer
- 用于初始化狀態(tài)、加工狀態(tài);
- 加工時(shí),根據(jù)舊的 state 和 action,產(chǎn)生新的 state 的純函數(shù);
- 初始化時(shí),previousState 為 undefined。
store
將state 、action 、reducer聯(lián)系在一起的對(duì)象;
如何得到此對(duì)象?
如何得到此對(duì)象?
import { createStore } from 'redux'
//
import reducer from './reducers'
//
const store = createStore(reducer)
3.此對(duì)象的功能?
- getState():得到 state;
- dispatch(action):分發(fā) action,觸發(fā) reducer 調(diào)用,產(chǎn)生新的 state;
- subscribe(listener):注冊(cè)監(jiān)聽(tīng),當(dāng)產(chǎn)生了新的 state 時(shí),自動(dòng)調(diào)用。

select選擇框內(nèi)有三個(gè)數(shù)組1、2、3可選,選擇1后點(diǎn)擊“加”即和加1;
第三個(gè)按鈕為和為奇數(shù)時(shí),則加;和為偶數(shù)時(shí),則不變;
最后的“異步加”則是用setTimeout模擬了一個(gè)異步環(huán)境,點(diǎn)擊“異步加”等待0.5s再加。
求和案例——純r(jià)eact版
項(xiàng)目目錄:
src ├─App.jsx ├─index.js ├─components | ├─Count | | └index.jsx public └index.html
使用create-react-app創(chuàng)建腳手架項(xiàng)目,項(xiàng)目目錄如上所示。
純r(jià)eact版是通過(guò)本組件的state獲取數(shù)據(jù),在此不過(guò)多贅述。
歡迎訂閱本專欄【React–從基礎(chǔ)到實(shí)戰(zhàn)】學(xué)習(xí)react知識(shí),持續(xù)更新中!
App.jsx
import React, { Component } from 'react'
import Count from './components/Count'
export default class App extends Component {
render() {
return (
<div>
<Count />
</div>
)
}
}
src/component/Count/index.jsx
import React, { Component } from 'react'
export default class Count extends Component {
state = { count: 0 }
// 加法
increment = () => {
const { value } = this.selectedNumber
const { count } = this.state
this.setState({ count: count + parseInt(value) });
}
// 減法
decrement = () => {
const { value } = this.selectedNumber
const { count } = this.state
this.setState({ count: count - value });
}
// 和為奇數(shù)時(shí),加
incrementIfOdd = () => {
const { value } = this.selectedNumber
const { count } = this.state
// 除以2取余數(shù),余數(shù)為0代表和為偶數(shù),不加;余數(shù)不為0,代表和為奇數(shù),加
if (count % 2 !== 0) {
this.setState({ count: count + parseInt(value) });
}
}
// 異步加
incrementAsync = () => {
const { value } = this.selectedNumber
const { count } = this.state
setTimeout(() => {
this.setState({ count: count + parseInt(value) });
}, 500)
}
render() {
return (
<div>
<h1>當(dāng)前求和為:{this.state.count}</h1>
<select ref={currentNode => { this.selectedNumber = currentNode }}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>加</button>
<button onClick={this.decrement}>減</button>
<button onClick={this.incrementIfOdd}>和為奇數(shù)時(shí),加</button>
<button onClick={this.incrementAsync}>異步加</button>
</div>
)
}
}
求和案例——redux精簡(jiǎn)版
為了讓大家更好的理解redux基本原理,我們先僅使用少數(shù)的api,用過(guò)一個(gè)精簡(jiǎn)版的redux去完成這個(gè)求和案例,最后我們會(huì)用完整版的redux去完成這個(gè)求和案例。學(xué)東西嘛,得循序漸進(jìn),不能一口吃成大胖子不是?
首先,我們需要下載redux:
npm install redux 或 yarn add redux
根據(jù)原理圖,我們必不可少的肯定是需要store這個(gè)對(duì)象,以及reducer這個(gè)函數(shù)的。
store對(duì)象負(fù)責(zé)存儲(chǔ)state,reducer負(fù)責(zé)初始化和加工state,都是必不可少的。
1、創(chuàng)建store.js
/*
該文件專門用于暴露一個(gè)store對(duì)象,整個(gè)應(yīng)用只有一個(gè)store對(duì)象
*/
// 引入createStore,專門用于創(chuàng)建redux中最為核心的store對(duì)象
import { legacy_createStore as createStore } from 'redux';
// 引入為Count組件服務(wù)的reducer
import countReducer from './count_reducer'
// 暴露store
export default createStore(countReducer)
2、創(chuàng)建count_reducer.js
/*
1.該文件用于創(chuàng)建一個(gè)為Count組件服務(wù)的reducer函數(shù)
2.reducer函數(shù)會(huì)接收到兩個(gè)參數(shù),分別為之前的狀態(tài)(prevState)和動(dòng)作對(duì)象(action)
*/
// 初始化state,prevState=initState含義是:給形參prevState賦初始值
const initState = 0
export default function countReducer(prevState = initState, action) {
console.log(prevState, action)
// 從action對(duì)象中獲取type、data
const { type, data } = action
// 根據(jù)type的值決定如何加工數(shù)據(jù)
switch (type) {
case 'increment':
return prevState + data;
case 'decrement':
return prevState - data;
default:
return prevState
}
}
3、回到Count組件中,我們先試著獲取到store中存儲(chǔ)的數(shù)據(jù)。
// 引入store,用于獲取redux中保存的狀態(tài)
import store from '../../redux/store'
···
render() {
return (
<div>
// <h1>當(dāng)前求和為:{this.state.count}</h1>
<h1>當(dāng)前求和為:{store.getState()}</h1>
···
現(xiàn)在我們把狀態(tài)(state)交給redux管理了,當(dāng)然就不需要在Count組件中初始化狀態(tài)了,也就不需要再到this.state中結(jié)構(gòu)賦值count變量了。
我們只需要調(diào)用redux的api:store.getState()就能夠拿到存儲(chǔ)在store中的狀態(tài)。
打開(kāi)界面一看:

果然是拿到了狀態(tài)state,那么我們就著手依次進(jìn)行計(jì)算操作了~
首先是,點(diǎn)擊“加”按鈕,觸發(fā)this.increment方法,我們?cè)诜椒▋?nèi)部只需要調(diào)用redux的api:dispatch()即可。
// 加法
increment = () => {
const { value } = this.selectedNumber
store.dispatch({ type: 'increment', data: parseInt(value) })
}
我們通過(guò)調(diào)用dispatch()方法,傳入一個(gè)我們自己定義的action動(dòng)作對(duì)象,進(jìn)行“加”的動(dòng)作,這個(gè)動(dòng)作對(duì)象會(huì)由store傳給reducer函數(shù),函數(shù)內(nèi)部判斷type值為‘increment’,那么就會(huì)對(duì)這個(gè)‘和’進(jìn)行“加”的動(dòng)作
代碼實(shí)現(xiàn)完了,我們看一下效果:

完?duì)僮樱@怎么沒(méi)加上去呢?
我們?cè)赾ount_reducer中console一下,找找原因,

打開(kāi)控制臺(tái),我們發(fā)現(xiàn)狀態(tài)是在跟隨我們的點(diǎn)擊隨之改變的,但是頁(yè)面并沒(méi)有同步這個(gè)狀態(tài)的改變。換句話說(shuō),也就是狀態(tài)改變了,頁(yè)面并沒(méi)有隨著狀態(tài)的改變而重新渲染。在之前的純r(jià)eact版本中,我們是通過(guò)this.setState()進(jìn)行狀態(tài)更新的,我們都知道react中,setState可以驅(qū)動(dòng)視圖更新,但是我們現(xiàn)在并不在組件的state中管理狀態(tài),而是通過(guò)redux進(jìn)行狀態(tài)的管理,所以才會(huì)導(dǎo)致頁(yè)面沒(méi)有更新。
我們需要手動(dòng)監(jiān)測(cè)redux中store狀態(tài),當(dāng)狀態(tài)發(fā)生變化時(shí),我們自己去手動(dòng)調(diào)用render,就可以解決頁(yè)面不更新的問(wèn)題。
componentDidMount() {
// 監(jiān)測(cè)redux中狀態(tài)的變化,只要變化,就調(diào)用render
store.subscribe(() => {
this.setState({})
})
}
我們可以使用這個(gè)api:store.subscribe()這個(gè)函數(shù)的參數(shù)接受一個(gè)回調(diào)函數(shù),只要redux中的任何狀態(tài)發(fā)生了改變,都會(huì)調(diào)用這個(gè)回調(diào)函數(shù)。我們可以在這個(gè)回調(diào)函數(shù)內(nèi)部手動(dòng)調(diào)用this.setState({}),只要這么調(diào)用了,就會(huì)自動(dòng)重新render,這樣就可以實(shí)現(xiàn)頁(yè)面的更新了。
Count組件源碼:
import React, { Component } from 'react'
// 引入store,用于獲取redux中保存的狀態(tài)
import store from '../../redux/store'
export default class Count extends Component {
componentDidMount() {
// 監(jiān)測(cè)redux中狀態(tài)的變化,只要變化,就調(diào)用render
store.subscribe(() => {
this.setState({})
})
}
// 加法
increment = () => {
const { value } = this.selectedNumber
store.dispatch({ type: 'increment', data: value * 1 })
}
// 減法
decrement = () => {
const { value } = this.selectedNumber
store.dispatch({ type: 'decrement', data: value * 1 })
}
// 和為奇數(shù)時(shí),加
incrementIfOdd = () => {
const { value } = this.selectedNumber
const count = store.getState()
// 除以2取余數(shù),余數(shù)為0代表和為偶數(shù),不加;余數(shù)不為0,代表和為奇數(shù),加
if (count % 2 !== 0) {
store.dispatch({ type: 'increment', data: value * 1 })
}
}
// 異步加
incrementAsync = () => {
const { value } = this.selectedNumber
setTimeout(() => {
store.dispatch({ type: 'increment', data: value * 1 })
}, 500)
}
render() {
return (
<div>
<h1>當(dāng)前求和為:{store.getState()}</h1>
<select ref={currentNode => { this.selectedNumber = currentNode }}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>加</button>
<button onClick={this.decrement}>減</button>
<button onClick={this.incrementIfOdd}>和為奇數(shù)時(shí),加</button>
<button onClick={this.incrementAsync}>異步加</button>
</div>
)
}
}
redux精簡(jiǎn)版總結(jié):
去除Count組件自身狀態(tài)state;
src下建立:
/src/redux/store.js
/src/reudx/count_reducer.js
store.js
引入redux中的legacy_createStore函數(shù),創(chuàng)建一個(gè)store;
legacy_createStore調(diào)用時(shí)要傳入一個(gè)為其服務(wù)的reducer;
默認(rèn)暴露store對(duì)象
count_reducer.js
reducer本質(zhì)是一個(gè)函數(shù),接收:preState,action作為參數(shù),返回加工后的狀態(tài);
reducer有兩個(gè)作用:初始化狀態(tài),加工狀態(tài);
reducer被第一次調(diào)用,是store自動(dòng)觸發(fā)的,傳遞的preState是undefined
狀態(tài)改變后手動(dòng)調(diào)用setState()以達(dá)到頁(yè)面實(shí)時(shí)渲染的效果。
求和案例——redux完整版
在精簡(jiǎn)版中,我們使用的action對(duì)象是我們自己手動(dòng)傳入的action動(dòng)作對(duì)象,但是redux有更好的api,來(lái)幫助我們自動(dòng)創(chuàng)建action動(dòng)作對(duì)象,不需要手動(dòng)輸入。我們來(lái)看看redux完整版是怎么實(shí)現(xiàn)集中狀態(tài)管理的吧~
基于精簡(jiǎn)版,我們還需要在redux文件夾下創(chuàng)建一個(gè)文件
/src/redux/count_action_creator.js,該文件專門用于為Count組件生成action動(dòng)作對(duì)象。
count_action_creator.js
/*
該文件專門為Count組件生成action動(dòng)作對(duì)象
*/
export const createIncrementAction = data => ({ type: 'increment', data })
// ({ type: 'increment', data }) 相當(dāng)于 { return { type: 'increment', data } }
export const createDecrementAction = data => ({ type: 'decrement', data })
在Count組件中引入文件以及暴露的函數(shù):
// 引入actionCreator,專門用于創(chuàng)建action對(duì)象
import {
createIncrementAction,
createDecrementAction
} from '../../redux/count_action_creator'
在所有需要使用dispatch分派action動(dòng)作對(duì)象時(shí),我們不再使用之前手打的action對(duì)象了,而是直接調(diào)用函數(shù),自動(dòng)生成action對(duì)象。
例如,在點(diǎn)擊“加”按鈕方法中:
// 加法
increment = () => {
const { value } = this.selectedNumber
store.dispatch(createIncrementAction(value * 1))
}
以此類推,將所有方法內(nèi)手打的action對(duì)象更改為使用函數(shù)自動(dòng)生成的action對(duì)象。
Count組件源碼:
import React, { Component } from 'react'
// 引入store,用于獲取redux中保存的狀態(tài)
import store from '../../redux/store'
// 引入actionCreator,專門用于創(chuàng)建action對(duì)象
import {
createIncrementAction,
createDecrementAction
} from '../../redux/count_action_creator'
export default class Count extends Component {
componentDidMount() {
// 監(jiān)測(cè)redux中狀態(tài)的變化,只要變化,就調(diào)用render
store.subscribe(() => {
this.setState({})
})
}
// 加法
increment = () => {
const { value } = this.selectedNumber
store.dispatch(createIncrementAction(value * 1))
}
// 減法
decrement = () => {
const { value } = this.selectedNumber
store.dispatch(createDecrementAction(value * 1))
}
// 和為奇數(shù)時(shí),加
incrementIfOdd = () => {
const { value } = this.selectedNumber
const count = store.getState()
// 除以2取余數(shù),余數(shù)為0代表和為偶數(shù),不加;余數(shù)不為0,代表和為奇數(shù),加
if (count % 2 !== 0) {
store.dispatch(createIncrementAction(value * 1))
}
}
// 異步加
incrementAsync = () => {
const { value } = this.selectedNumber
setTimeout(() => {
store.dispatch(createIncrementAction(value * 1))
}, 500)
}
render() {
return (
<div>
<h1>當(dāng)前求和為:{store.getState()}</h1>
<select ref={currentNode => { this.selectedNumber = currentNode }}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>加</button>
<button onClick={this.decrement}>減</button>
<button onClick={this.incrementIfOdd}>和為奇數(shù)時(shí),加</button>
<button onClick={this.incrementAsync}>異步加</button>
</div>
)
}
}
這樣一來(lái),基本的redux完整版就已經(jīng)寫(xiě)完了;但是還有一個(gè)可優(yōu)化的空間,我們?cè)诟鱾€(gè)文件中都寫(xiě)了’increment’和’decrement’字符串,這會(huì)帶來(lái)一些問(wèn)題:
- 字符串在IDE中沒(méi)有變量提示,容易拼寫(xiě)錯(cuò)誤;
- 不便于維護(hù)和管理,一旦要更換字符串名稱,需要到各個(gè)文件中都去更改一次; redux完整版(優(yōu)化)
于是我們可以再創(chuàng)建一個(gè)js文件,用于常量的管理(常量維護(hù)):
/src/redux/constant.js
/* 該模塊是用于定義action對(duì)象中type類型的常量模塊 便于管理的同時(shí)避免程序員單詞拼寫(xiě)出錯(cuò) */ export const INCREMENT = 'increment' export const DECREMENT = 'decrement'
更改,count_action_creator.js
import { INCREMENT, DECREMENT } from './constant'
export const createIncrementAction = data => ({ type: INCREMENT, data })
export const createDecrementAction = data => ({ type: DECREMENT, data })
更改,count_reducer.js
+ import { INCREMENT, DECREMENT } from './constant'
const initState = 0
export default function countReducer(prevState = initState, action) {
console.log(prevState, action)
const { type, data } = action
switch (type) {
- case 'increment':
+ case INCREMENT:
return prevState + data;
- case 'decrement':
+ case DECREMENT:
return prevState - data;
default:
return prevState
}
}
如此一來(lái),有利于后續(xù)項(xiàng)目越來(lái)越大的時(shí)候,進(jìn)行變量名的管理與維護(hù);而且,書(shū)寫(xiě)字符串是沒(méi)有提示的,但是向這樣引入變量,書(shū)寫(xiě)變量是有提示的,變量書(shū)寫(xiě)錯(cuò)誤的話,IDE還有相應(yīng)的錯(cuò)誤提示,更加規(guī)范。
redux完整版總結(jié):
- count_action.js 專門用于創(chuàng)建action動(dòng)作對(duì)象;
- constant.js 放置由于編碼疏忽可能寫(xiě)錯(cuò)的action中的type。
到此這篇關(guān)于Redux的工作流程的文章就介紹到這了,更多相關(guān)Redux工作流程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React中的useEffect useLayoutEffect到底怎么用
這篇文章主要介紹了React中的useEffect useLayoutEffect具體使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2023-02-02
解決react中useState狀態(tài)異步更新的問(wèn)題
本文主要介紹了react中useState狀態(tài)異步更新的問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
React Router6.x路由表封裝的兩種寫(xiě)法
本文主要介紹了React Router6.x路由表封裝的兩種寫(xiě)法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01
React hooks如何清除定時(shí)器并驗(yàn)證效果
在React中,通過(guò)自定義Hook useTimeHook實(shí)現(xiàn)定時(shí)器的啟動(dòng)與清除,在App組件中使用Clock組件展示當(dāng)前時(shí)間,利用useEffect鉤子在組件掛載時(shí)啟動(dòng)定時(shí)器,同時(shí)確保組件卸載時(shí)清除定時(shí)器,避免內(nèi)存泄露,這種方式簡(jiǎn)化了狀態(tài)管理和副作用的處理2024-10-10
react實(shí)現(xiàn)一個(gè)優(yōu)雅的圖片占位模塊組件詳解
這篇文章主要給大家介紹了關(guān)于react如何實(shí)現(xiàn)一個(gè)還算優(yōu)雅的占位模塊圖片組件的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-10-10
React+EggJs實(shí)現(xiàn)斷點(diǎn)續(xù)傳的示例代碼
這篇文章主要介紹了React+EggJs實(shí)現(xiàn)斷點(diǎn)續(xù)傳的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
React如何使用Portal實(shí)現(xiàn)跨層級(jí)DOM渲染
Portal 就像是一個(gè)“傳送門”,能讓你把組件里的元素“傳送到”其他 DOM 節(jié)點(diǎn)下面去渲染,下面小編就來(lái)和大家簡(jiǎn)單介紹一下具體的使用方法吧2025-04-04

