詳解Redux的工作流程
(當(dāng)然不是這張圖)
Redux理解
redux是什么
- redux是一個專門用于做狀態(tài)管理的JS庫(不是react插件庫);
- 它可以在react、angular、vue等項(xiàng)目中,但基本與react配合使用;
- 作用:集中式管理react應(yīng)用中多個組件共享的狀態(tài)。
什么情況下需要使用redux?
- 某個組件的狀態(tài),需要讓其他組件可以隨時拿到(共享);
- 一個組件需要改變另一個組件的狀態(tài)(通信);
- 總體原則:能不用就不用,如果不用比較吃力才考慮使用。
redux工作流程??圖
action
- 動作的對象
- 包含兩個屬性:
- type:標(biāo)識屬性,值為字符串,唯一,必要屬性;
- data:數(shù)據(jù)屬性,值類型任意,可選屬性。
- 例子:{ type:‘ADD_STUDENT’, data:{name: ‘tom’,age: 18} }
reducer
- 用于初始化狀態(tài)、加工狀態(tài);
- 加工時,根據(jù)舊的 state 和 action,產(chǎn)生新的 state 的純函數(shù);
- 初始化時,previousState 為 undefined。
store
將state 、action 、reducer聯(lián)系在一起的對象;
如何得到此對象?
如何得到此對象?
import { createStore } from 'redux' // import reducer from './reducers' // const store = createStore(reducer)
3.此對象的功能?
- getState():得到 state;
- dispatch(action):分發(fā) action,觸發(fā) reducer 調(diào)用,產(chǎn)生新的 state;
- subscribe(listener):注冊監(jiān)聽,當(dāng)產(chǎn)生了新的 state 時,自動調(diào)用。
select選擇框內(nèi)有三個數(shù)組1、2、3可選,選擇1后點(diǎn)擊“加”即和加1;
第三個按鈕為和為奇數(shù)時,則加;和為偶數(shù)時,則不變;
最后的“異步加”則是用setTimeout模擬了一個異步環(huán)境,點(diǎn)擊“異步加”等待0.5s再加。
求和案例——純react版
項(xiàng)目目錄:
src ├─App.jsx ├─index.js ├─components | ├─Count | | └index.jsx public └index.html
使用create-react-app創(chuàng)建腳手架項(xiàng)目,項(xiàng)目目錄如上所示。
純react版是通過本組件的state獲取數(shù)據(jù),在此不過多贅述。
歡迎訂閱本專欄【React–從基礎(chǔ)到實(shí)戰(zhàn)】學(xué)習(xí)react知識,持續(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ù)時,加 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ù)時,加</button> <button onClick={this.incrementAsync}>異步加</button> </div> ) } }
求和案例——redux精簡版
為了讓大家更好的理解redux基本原理,我們先僅使用少數(shù)的api,用過一個精簡版的redux去完成這個求和案例,最后我們會用完整版的redux去完成這個求和案例。學(xué)東西嘛,得循序漸進(jìn),不能一口吃成大胖子不是?
首先,我們需要下載redux:
npm install redux 或 yarn add redux
根據(jù)原理圖,我們必不可少的肯定是需要store這個對象,以及reducer這個函數(shù)的。
store對象負(fù)責(zé)存儲state,reducer負(fù)責(zé)初始化和加工state,都是必不可少的。
1、創(chuàng)建store.js
/* 該文件專門用于暴露一個store對象,整個應(yīng)用只有一個store對象 */ // 引入createStore,專門用于創(chuàng)建redux中最為核心的store對象 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)建一個為Count組件服務(wù)的reducer函數(shù) 2.reducer函數(shù)會接收到兩個參數(shù),分別為之前的狀態(tài)(prevState)和動作對象(action) */ // 初始化state,prevState=initState含義是:給形參prevState賦初始值 const initState = 0 export default function countReducer(prevState = initState, action) { console.log(prevState, action) // 從action對象中獲取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中存儲的數(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()
就能夠拿到存儲在store中的狀態(tài)。
打開界面一看:
果然是拿到了狀態(tài)state,那么我們就著手依次進(jìn)行計(jì)算操作了~
首先是,點(diǎn)擊“加”按鈕,觸發(fā)this.increment方法,我們在方法內(nèi)部只需要調(diào)用redux的api:dispatch()即可。
// 加法 increment = () => { const { value } = this.selectedNumber store.dispatch({ type: 'increment', data: parseInt(value) }) }
我們通過調(diào)用dispatch()方法,傳入一個我們自己定義的action動作對象,進(jìn)行“加”的動作,這個動作對象會由store傳給reducer函數(shù),函數(shù)內(nèi)部判斷type值為‘increment’,那么就會對這個‘和’進(jìn)行“加”的動作
代碼實(shí)現(xiàn)完了,我們看一下效果:
完?duì)僮樱@怎么沒加上去呢?
我們在count_reducer中console一下,找找原因,
打開控制臺,我們發(fā)現(xiàn)狀態(tài)是在跟隨我們的點(diǎn)擊隨之改變的,但是頁面并沒有同步這個狀態(tài)的改變。換句話說,也就是狀態(tài)改變了,頁面并沒有隨著狀態(tài)的改變而重新渲染。在之前的純react版本中,我們是通過this.setState()
進(jìn)行狀態(tài)更新的,我們都知道react中,setState可以驅(qū)動視圖更新,但是我們現(xiàn)在并不在組件的state中管理狀態(tài),而是通過redux進(jìn)行狀態(tài)的管理,所以才會導(dǎo)致頁面沒有更新。
我們需要手動監(jiān)測redux中store狀態(tài),當(dāng)狀態(tài)發(fā)生變化時,我們自己去手動調(diào)用render,就可以解決頁面不更新的問題。
componentDidMount() { // 監(jiān)測redux中狀態(tài)的變化,只要變化,就調(diào)用render store.subscribe(() => { this.setState({}) }) }
我們可以使用這個api:store.subscribe()
這個函數(shù)的參數(shù)接受一個回調(diào)函數(shù),只要redux中的任何狀態(tài)發(fā)生了改變,都會調(diào)用這個回調(diào)函數(shù)。我們可以在這個回調(diào)函數(shù)內(nèi)部手動調(diào)用this.setState({})
,只要這么調(diào)用了,就會自動重新render,這樣就可以實(shí)現(xiàn)頁面的更新了。
Count組件源碼:
import React, { Component } from 'react' // 引入store,用于獲取redux中保存的狀態(tài) import store from '../../redux/store' export default class Count extends Component { componentDidMount() { // 監(jiān)測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ù)時,加 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ù)時,加</button> <button onClick={this.incrementAsync}>異步加</button> </div> ) } }
redux精簡版總結(jié):
去除Count組件自身狀態(tài)state;
src下建立:
/src/redux/store.js
/src/reudx/count_reducer.js
store.js
引入redux中的legacy_createStore函數(shù),創(chuàng)建一個store;
legacy_createStore調(diào)用時要傳入一個為其服務(wù)的reducer;
默認(rèn)暴露store對象
count_reducer.js
reducer本質(zhì)是一個函數(shù),接收:preState,action作為參數(shù),返回加工后的狀態(tài);
reducer有兩個作用:初始化狀態(tài),加工狀態(tài);
reducer被第一次調(diào)用,是store自動觸發(fā)的,傳遞的preState是undefined
狀態(tài)改變后手動調(diào)用setState()以達(dá)到頁面實(shí)時渲染的效果。
求和案例——redux完整版
在精簡版中,我們使用的action對象是我們自己手動傳入的action動作對象,但是redux有更好的api,來幫助我們自動創(chuàng)建action動作對象,不需要手動輸入。我們來看看redux完整版是怎么實(shí)現(xiàn)集中狀態(tài)管理的吧~
基于精簡版,我們還需要在redux文件夾下創(chuàng)建一個文件
/src/redux/count_action_creator.js,該文件專門用于為Count組件生成action動作對象。
count_action_creator.js
/* 該文件專門為Count組件生成action動作對象 */ 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對象 import { createIncrementAction, createDecrementAction } from '../../redux/count_action_creator'
在所有需要使用dispatch分派action動作對象時,我們不再使用之前手打的action對象了,而是直接調(diào)用函數(shù),自動生成action對象。
例如,在點(diǎn)擊“加”按鈕方法中:
// 加法 increment = () => { const { value } = this.selectedNumber store.dispatch(createIncrementAction(value * 1)) }
以此類推,將所有方法內(nèi)手打的action對象更改為使用函數(shù)自動生成的action對象。
Count組件源碼:
import React, { Component } from 'react' // 引入store,用于獲取redux中保存的狀態(tài) import store from '../../redux/store' // 引入actionCreator,專門用于創(chuàng)建action對象 import { createIncrementAction, createDecrementAction } from '../../redux/count_action_creator' export default class Count extends Component { componentDidMount() { // 監(jiān)測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ù)時,加 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ù)時,加</button> <button onClick={this.incrementAsync}>異步加</button> </div> ) } }
這樣一來,基本的redux完整版就已經(jīng)寫完了;但是還有一個可優(yōu)化的空間,我們在各個文件中都寫了’increment’和’decrement’字符串,這會帶來一些問題:
- 字符串在IDE中沒有變量提示,容易拼寫錯誤;
- 不便于維護(hù)和管理,一旦要更換字符串名稱,需要到各個文件中都去更改一次; redux完整版(優(yōu)化)
于是我們可以再創(chuàng)建一個js文件,用于常量的管理(常量維護(hù)):
/src/redux/constant.js
/* 該模塊是用于定義action對象中type類型的常量模塊 便于管理的同時避免程序員單詞拼寫出錯 */ 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 } }
如此一來,有利于后續(xù)項(xiàng)目越來越大的時候,進(jìn)行變量名的管理與維護(hù);而且,書寫字符串是沒有提示的,但是向這樣引入變量,書寫變量是有提示的,變量書寫錯誤的話,IDE還有相應(yīng)的錯誤提示,更加規(guī)范。
redux完整版總結(jié):
- count_action.js 專門用于創(chuàng)建action動作對象;
- constant.js 放置由于編碼疏忽可能寫錯的action中的type。
到此這篇關(guān)于Redux的工作流程的文章就介紹到這了,更多相關(guān)Redux工作流程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react中的watch監(jiān)視屬性-useEffect介紹
這篇文章主要介紹了react中的watch監(jiān)視屬性-useEffect使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09react echarts tooltip 區(qū)域新加輸入框編輯保存數(shù)據(jù)功能
這篇文章主要介紹了react echarts tooltip 區(qū)域新加輸入框編輯保存數(shù)據(jù)功能,大概思路是用一個div包裹echarts, 然后在echarts的同級新建一個div用來用來模擬真實(shí)tooltip,通過鼠標(biāo)移入移出事件控制真實(shí)tooltip的顯示與隱藏,需要的朋友可以參考下2023-05-05React項(xiàng)目中hook實(shí)現(xiàn)展示對話框功能
Modal(模態(tài)框)是 web 開發(fā)中十分常見的組件,即從頁面中彈出的對話框,下面這篇文章主要給大家介紹了關(guān)于React項(xiàng)目中hook實(shí)現(xiàn)展示對話框功能的相關(guān)資料,需要的朋友可以參考下2022-05-05React避坑指南之useEffect 依賴引用類型問題分析
這篇文章主要介紹了React避坑指南之useEffect 依賴引用類型問題分析,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-03react-native-fs實(shí)現(xiàn)文件下載、文本存儲的示例代碼
本篇文章主要介紹了react-native-fs實(shí)現(xiàn)文件下載、文本存儲的示例代碼,具有一定的參考價值,有興趣的可以了解下2017-09-09react實(shí)現(xiàn)antd線上主題動態(tài)切換功能
這篇文章主要介紹了react實(shí)現(xiàn)antd線上主題動態(tài)切換功能,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-08-08基于Webpack4和React hooks搭建項(xiàng)目的方法
這篇文章主要介紹了基于Webpack4和React hooks搭建項(xiàng)目的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-02-02使用react實(shí)現(xiàn)手機(jī)號的數(shù)據(jù)同步顯示功能的示例代碼
本篇文章主要介紹了使用react實(shí)現(xiàn)手機(jī)號的數(shù)據(jù)同步顯示功能的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-04-04