React狀態(tài)管理Redux原理與介紹
一、Redux
和vuex一樣,redux的出現(xiàn)是為了管理web應(yīng)用的公共狀態(tài)。
這些 state 可能包括服務(wù)器響應(yīng)、緩存數(shù)據(jù)、本地生成尚未持久化到服務(wù)器的數(shù)據(jù),也包括 UI 狀態(tài),如激活的路由,被選中的標(biāo)簽,是否顯示加載動(dòng)效或者分頁(yè)器等等。

二、Redux的組成
2.1 store
store 就是保存數(shù)據(jù)的地方,整個(gè)應(yīng)用只能有一個(gè) store,可以理解為一個(gè)存儲(chǔ)數(shù)據(jù)的倉(cāng)庫(kù)。
redux 提供 createStore 這個(gè)函數(shù),用來(lái)創(chuàng)建一個(gè)store 以存放整個(gè)應(yīng)用的 state:
import { createStore } from 'redux';
const store = createStore(reducer, [preloadedState], [enhancer]);
可以看到,createStore 接受 reducer、初始 state(可選)和增強(qiáng)器(可選)作為參數(shù),返回一個(gè)新的 store 對(duì)象.
2.2 state
state就是store 對(duì)象包含所有數(shù)據(jù),如果要獲取當(dāng)前時(shí)刻的 state,可以通過(guò) store.getState() 方法拿到:
import { createStore } from 'redux';
const store = createStore(reducer, [preloadedState], [enhancer]);
const state = store.getState();2.3 action
- state 的變化,會(huì)導(dǎo)致視圖的變化。但是,用戶接觸不到 state,只能接觸到視圖。所以,state 的變化必須是由視圖發(fā)起的。
- action 就是視圖發(fā)出的通知,通知store此時(shí)的 state 應(yīng)該要發(fā)生變化了。
- action 是一個(gè)對(duì)象。其中的 type屬性是必須的,表示 action 的名稱。其他屬性可以自由設(shè)置,社區(qū)有一個(gè)規(guī)范可以參考:
const action = {
type: 'ADD_TODO',
payload: 'Learn Redux' // 可選屬性 可自定義名稱
};
所以action可以理解為視圖層向store發(fā)送的一個(gè)命令(通知),它包含了需要執(zhí)行的事件(type屬性)以及傳遞的數(shù)據(jù)(自定義屬性)。
2.4 reducer
- store 收到 action 以后,必須給出一個(gè)新的 state,這樣視圖才會(huì)進(jìn)行更新。state 的計(jì)算(更新)過(guò)程則是通過(guò)reducer 實(shí)現(xiàn)。
- reducer 是一個(gè)函數(shù),它接受 action 和當(dāng)前 state 作為參數(shù),返回一個(gè)新的 state:
const reducer = function (state = initState, action) {
// ...
return new_state;
};
創(chuàng)建store時(shí),第一個(gè)參數(shù)就是reducer:
const store = createStore(reducer);
那么如何向store發(fā)送action呢?
store.dispatch({
type: 'ADD_TODO',
payload: 'Learn Redux'
});
store對(duì)象擁有dispath方法發(fā)送action,參數(shù)就是需要傳遞的action對(duì)象,然后reducer接收到action并處理,返回新的state,視圖自動(dòng)更新。
三、三大原則
Redux 可以用這三個(gè)基本原則來(lái)描述:
3.1 單一數(shù)據(jù)源
整個(gè)應(yīng)用的 state 被儲(chǔ)存在一棵 object tree 中,并且這個(gè) object tree 只存在于唯一一個(gè) store 中。
console.log(store.getState())
/* 輸出
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true,
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
*/3.2 State只讀
唯一改變 state 的方法就是觸發(fā) action,action 是一個(gè)用于描述已發(fā)生事件的普通對(duì)象。
這樣確保了視圖和網(wǎng)絡(luò)請(qǐng)求都不能直接修改 state,相反它們只能表達(dá)想要修改的意圖。因?yàn)樗械男薷亩急患谢幚?,且?yán)格按照一個(gè)接一個(gè)的順序執(zhí)行,因此不用擔(dān)心競(jìng)態(tài)條件(race condition)的出現(xiàn)。 Action 就是普通對(duì)象而已,因此它們可以被日志打印、序列化、儲(chǔ)存、后期調(diào)試或測(cè)試時(shí)回放出來(lái)。
store.dispatch({
type: 'COMPLETE_TODO',
index: 1
})
store.dispatch({
type: 'SET_VISIBILITY_FILTER',
filter: 'SHOW_COMPLETED'
})所以返回新的state時(shí)不能直接修改參數(shù)state,而是在不修改參數(shù)state的基礎(chǔ)上返回新的state。
例如完成一個(gè)新增todo的功能:
case 'ADD_TODO':
return state.push({
text: action.text,
completed: false
})
這樣是不會(huì)生效的,應(yīng)為這樣直接修改了state的值,正確的做法應(yīng)該是:
case 'ADD_TODO':
return [
...state,
{
text: action.text,
completed: false
}
]
這里使用了擴(kuò)展運(yùn)算符(…)將數(shù)組展開(kāi)然后和新增的todo合并,對(duì)象同樣可以使用擴(kuò)展運(yùn)算符達(dá)到新增屬性的目的。
3.3 使用純函數(shù)修改State
為了描述 action 如何改變 state tree ,你需要編寫(xiě) reducers。
Reducer 只是一些純函數(shù),它接收先前的 state 和 action,并返回新的 state。剛開(kāi)始你可以只有一個(gè) reducer,隨著應(yīng)用變大,你可以把它拆成多個(gè)小的 reducers,分別獨(dú)立地操作 state tree 的不同部分,因?yàn)?reducer 只是函數(shù),你可以控制它們被調(diào)用的順序,傳入附加數(shù)據(jù),甚至編寫(xiě)可復(fù)用的 reducer 來(lái)處理一些通用任務(wù),如分頁(yè)器。
function visibilityFilter(state = 'SHOW_ALL', action) {
switch (action.type) {
case 'SET_VISIBILITY_FILTER':
return action.filter
default:
return state
}
}
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
{
text: action.text,
completed: false
}
]
case 'COMPLETE_TODO':
return state.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: true
})
}
return todo
})
default:
return state
}
}
import { combineReducers, createStore } from 'redux'
//合并reducer
let reducer = combineReducers({ visibilityFilter, todos })
//利用reducer創(chuàng)建store
let store = createStore(reducer)
四、基于Redux的TodoList
效果:

todes的reducer:
const initTodos = [
{
id: 1,
text: "睡覺(jué)??",
completed: false,
},
{
id: 2,
text: "吃飯??",
completed: false,
},
{
id: 3,
text: "打豆豆??",
completed: true,
},
];
let nextTodoID = 4;
const todos = (state = initTodos, action) => {
switch (action.type) {
case "ADD_TODO":
return [
...state,
{
id: nextTodoID++,
text: action.text,
completed: false,
},
];
case "TOGGLE_TODO":
return state.map((todo) =>
todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
);
default:
return state;
}
};
export default todos;filter的reducer:
const visibilityFilter = (state = "SHOW_ALL", action) => {
switch (action.type) {
case "SET_VISIBILITY_FILTER":
return action.filter;
default:
return state;
}
};
export default visibilityFilter;詳細(xì)代碼地址:
https://github.com/YancyZhang30/react-redux-todos.git
五、react-redux
redux 并不是react專有的,其本身是一個(gè)可以結(jié)合 react,vue,angular 甚至是原生 javaScript 應(yīng)用使用的狀態(tài)庫(kù)。
react-redux是react官方提供了 react的redux適配庫(kù)(這個(gè)庫(kù)是可以選用的,也可以只用redux),使得我們能夠更好地在react應(yīng)用中使用redux來(lái)進(jìn)行全局狀態(tài)管理,使用react-redux主要是為了解決組件每次使用store中的數(shù)據(jù)時(shí)都必須先使用store.getState()來(lái)獲取state,然后必須使用store.subscribe()進(jìn)行訂閱的問(wèn)題。
react-redux 將所有組件分成 UI 組件和容器組件兩大類:
1、 UI 組件只負(fù)責(zé) UI 的呈現(xiàn),不含有狀態(tài)(this.state),所有數(shù)據(jù)都由 this.props 提供,且不使用任何 redux 的 API。
2、容器組件負(fù)責(zé)管理數(shù)據(jù)和業(yè)務(wù)邏輯,含有狀態(tài)(this.state),可使用 redux 的 API。
5.1 connect方法
react-redux 提供了 connect 方法,用于將 UI 組件生成容器組件,所以如果組件想要使用store中的state,就必須先使用connect方法與store進(jìn)行連接:
import {connect} from 'react-redux'
const Counter = (props) => {
return (
<div>
<p>計(jì)數(shù)器: {props.num}</p>
<div>
<button onClick={props.increatement}>加</button> | <button onClick={props.decreate}>減</button><br/>
</div>
</div>
)
}
//讀取數(shù)據(jù)
const mapStateToProps=(state)=>{
return{
num:state
}
}
//進(jìn)行觸發(fā)action
const mapDispathToProps=(dispatch)=>{
return {
increatement:()=>{dispatch(
{
type:"inCreateNum",
num:10
}
)},
decreate:()=>{dispatch(
{
type:"descment",
num:10
}
)}
}
}
// connect將組件與store連接。connect里的參數(shù)順序不能顛倒
export default connect(mapStateToProps,mapDispathToProps)(Counter)connect(mapStateToProps,mapDispathToProps)(Counter)中:
- mapStateToProps:mapStateToProps 是一個(gè)函數(shù),它的作用就是建立一個(gè)從 state對(duì)象(外部)到 UI 組件 props對(duì)象的映射關(guān)系。該函數(shù)會(huì)訂閱 整個(gè)應(yīng)用的 store,每當(dāng) state 更新的時(shí)候,就會(huì)自動(dòng)執(zhí)行,重新計(jì)算 UI 組件的參數(shù),從而觸發(fā) UI 組件的重新渲染。還可以使用第二個(gè)參數(shù)(可選),代表容器組件的 props 對(duì)象
- mapDispathToProps:mapDispatchToProps 是 connect 函數(shù)的第二個(gè)參數(shù),用來(lái)建立 UI 組件的參數(shù)到 store.dispatch 方法的映射。
- Counter:需要變成容器組件的UI組件,也就是需要連接store的組件。
5.2 Provider組件
- 使用 connect 方法生成容器組件以后,需要讓容器組件拿到 state 對(duì)象,才能生成 UI 組件 的參數(shù)。
- react-redux提供了 Provider 組件,可以讓容器組件拿到 state,注意只有被Provider組件包含的組件才能拿到state。
main.jsx:
import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from 'react-redux'
import Counter from "./Counter";
import ShowCounter from "./ShowCounter";
import reducer from "./store/counter";
import { legacy_createStore as createStore } from "redux";
const store = createStore(reducer);
ReactDOM.createRoot(document.getElementById('root')).render(
<Provider store={store}>
<React.StrictMode>
<Counter></Counter>
<ShowCounter></ShowCounter>
</React.StrictMode>
</Provider>
)此時(shí)Counter組件和ShowCounter組件都可以拿到state。
ShowCounter.jsx:
import React from "react";
import {connect} from "react-redux";
const ShowCounter = (props) => {
return (
<div>
<p>來(lái)自計(jì)數(shù)器的數(shù)據(jù):<span style={{color: 'red'}}>{props.num}</span></p>
</div>
)
}
//讀取數(shù)據(jù)
const mapStateToProps=(state)=>{
return{
num:state
}
}
// connect將組件與store連接。connect里的參數(shù)順序不能顛倒
export default connect(mapStateToProps)(ShowCounter)由于ShowCounter組件并不需要修改store,所以mapDispathToProps參數(shù)可以直接省略了。
效果:

到此這篇關(guān)于React狀態(tài)管理Redux原理與介紹的文章就介紹到這了,更多相關(guān)React Redux內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
搭建React?Native熱更新平臺(tái)的詳細(xì)過(guò)程
這篇文章主要介紹了搭建React?Native熱更新平臺(tái),要實(shí)現(xiàn)?React?Native?熱更新功能,有四種思路?Code?Push、Pushy、Expo?和自研,感興趣的朋友一起通過(guò)本文學(xué)習(xí)吧2022-05-05
React實(shí)現(xiàn)antdM的級(jí)聯(lián)菜單實(shí)例
這篇文章主要為大家介紹了React實(shí)現(xiàn)antdM的級(jí)聯(lián)菜單實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
React 項(xiàng)目中動(dòng)態(tài)設(shè)置環(huán)境變量
本文主要介紹了React 項(xiàng)目中動(dòng)態(tài)設(shè)置環(huán)境變量,本文將介紹兩種常用的方法,使用 dotenv 庫(kù)和通過(guò)命令行參數(shù)傳遞環(huán)境變量,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
React路由的history對(duì)象的插件history的使用解讀
這篇文章主要介紹了React路由的history對(duì)象的插件history的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10
詳解React?Native項(xiàng)目中啟用Hermes引擎
這篇文章主要為大家介紹了React?Native項(xiàng)目中啟用Hermes引擎實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
React路由參數(shù)傳遞與嵌套路由的實(shí)現(xiàn)詳細(xì)講解
這篇文章主要介紹了React路由參數(shù)傳遞與嵌套路由的實(shí)現(xiàn),嵌套路由原則是可以無(wú)限嵌套,但是必須要讓使用二級(jí)路由的一級(jí)路由匹配到,否則不顯示,需要的朋友可以參考一下2022-09-09
react開(kāi)發(fā)教程之React 組件之間的通信方式
本篇文章主要介紹了react開(kāi)發(fā)教程之React組件通信詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08
React通過(guò)hook實(shí)現(xiàn)封裝表格常用功能
這篇文章主要為大家詳細(xì)介紹了React通過(guò)hook封裝表格常用功能的使用,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,有需要的小伙伴可以參考下2023-12-12
React源碼state計(jì)算流程和優(yōu)先級(jí)實(shí)例解析
這篇文章主要為大家介紹了React源碼state計(jì)算流程和優(yōu)先級(jí)實(shí)例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11

