- Context API
- MobX
- Redux
React有自己狀態管理,周邊生態也有很多狀態管理
Context API
直接從react中引入并調用即可,context包含兩個東西:
Provider:提供商(翻譯),提供數據;屬性:value={提供商需要提供的數據}<Provider value={}>組件內容</Provider>Consumer:消費者(翻譯),使用數據;內部是函數<Consumer>{(value)=>{ return 組件內容 }}</Consumer>
測試Context:
import {Component} from 'react'
import {createContext} from 'react' //引入react自帶的狀態管理,得先創建它之后才可以去用
const {Provider,Consumer}=createContext(); //用于創建一個上下文class Child extends Component {render () {return (<div><h1>兒子</h1><Child2/></div>)}
}class Child2 extends Component {render () {return (//使用時包裹起來,標明身份,Consumer內部是個函數<Consumer>{(value)=>{return <h1>孫子,使用Provider提供的數據:{value.a}</h1>}}</Consumer>/*<div><h1>子組件2</h1></div>*/)}
}class Parent extends Component {render () {return (//使用時包裹起來,標明身份,Provider不要忘記value屬性<Provider value={{a:1}}><h1>父組件</h1><Child/></Provider>/*<div><h1>父組件</h1><Child/></div>*/)}
}export default Parent;
src\App.js
import './App.css';
import Routers from './router'
// import Parent from "./pages/home/context/myclass";
import ContextAPI from './store/index';function App() {return (<div className="App"><ContextAPI><Routers/></ContextAPI>{/*<Parent/> /!*狀態管理 - 理解Context*!/*/}</div>);
}export default App;
創建公共數據組件:src\store\index.js
import {Component,createContext} from 'react'const {Provider,Consumer}=createContext();class ContextAPI extends Component {constructor() {super();this.state = {a:1,b:2}}setA=(v)=>{this.setState({a:v,});}setB=(v)=>{this.setState({b:10,});}render() {return <Provider value={{ /*value通過這個屬性能夠對外提供公共的數據*/state:this.state,setA:this.setA,setB:this.setB}}>{this.props.children} {/*展示ContextAPI組件中間的子組件內容的*/}</Provider>}
}export default ContextAPI;
export {Consumer}; //導出使用者:Consumer
使用公共狀態:src\pages\home\notice\index.js
import {Consumer} from '../../../store/index';const Notice = () => {return <Consumer>{(value)=>{console.log(value)return <div><h1>Notice</h1><h2>a:{value.state.a}</h2> {/*使用提供者(公共組件)的數據*/}<h2>b:{value.state.b}</h2><button onClick={()=>{value.setA(25); /*調用提供者共享出來的方法*/value.setB();}}>修改數據</button></div>}}</Consumer>/*return <div><h1>Notice</h1></div>*/
}export default Notice;
總結:
1)創建公共數據的組件,設置數據和修改數據的方法并導出2)將該組件包裹住最大的組件,所有的組件都可以獲取到公共組件的數據了2.1)公共組件需要設置 {this.props.children} 才能展示包裹住的組件的內容2.2)不能在使用公共數據的組件里面自己重新創建 const {Consumer}=createContext()因為重新創建的Consumer和我們之前在公共組件中的 Consumer 不是同一個對象3)哪個組件要使用公共數據就引入import {Consumer} from "../../../store";參考:notice -> index_context.js
MobX
mobx分兩部分:
mobx:mobx語法
mobx-react:更新react的虛擬dom,把mobx和react連接起來
安裝:
安裝 mobx 和 mobx-react 或 mobx-react-lite
;mobx-react 和 mobx-react-lite 都是連接 react 和mobx的中間件,區別是:mobx-react支持類和函數組件,體積相對而言較大;而mobx-react-lite只支持函數組件,體積較小,
可以根據具體情況進行下載
npm i mobx mobx-react --save語法參考:<!-- https://mobx.js.org/README.html -->和<!-- https://github.com/mobxjs/mobx -->
引入:
import { makeAutoObservable } from "mobx" //makeAutoObservable:使自動可觀察
import { observer } from "mobx-react-lite" //observer:觀察者
使用mobx,創建store類,并實例化導出以供使用:src\store\index.js(class寫法)
import { makeAutoObservable } from "mobx" //使自動可觀察(可觀測的)
// import { observer } from "mobx-react-lite" //觀察者class Store {constructor() {makeAutoObservable(this); //統一所有的this指向的固定寫法}//定義數據a=1;b=2;//定義方法setA(v){this.a=v;}setB(){this.b=22;}//計算屬性get c(){ //添加計算屬性通過get關鍵詞實現return this.a+this.b;}}//實例化并導出
const store=new Store();
export default store;
mobx的函數組件的寫法:
import { makeAutoObservable } from "mobx"
import moduleMobx from './moduleMobx' //引入子模塊const store=()=>{return makeAutoObservable({//...moduleMobx //在組件上使用數據的時候寫法就是:store.dmoduleMobx, //store.moduleMobx.d//定義數據a:1,b:2,//定義方法setA(v){this.a=v;},setB(){this.b=22;},//計算屬性get c(){ //添加計算屬性通過get關鍵詞實現return this.a+this.b;}})}export default store();
模塊化:src\store\moduleMobx.js
const moduleMobx={ //直接導出notice.js需要使用的數據d:6,e:5,get dxe(){return this.d*this.e;},setD(v){this.d=v;}}export default moduleMobx;/*
如果直接:export default {}會出現黃色警告:Assign object to a variable before exporting as module default import/no-anonymous-default-export
*/
使用mobx-react,observer包裹組件:src\pages\notice\index.js
import store from '../../../store'; //哪個組件需要用到store的數據就在哪個組件引入
import {observer} from 'mobx-react'; //觀察者,被它包裹的組件就是響應式的,改了數據頁面就會更新const Notice = () => {return <div><h1>Notice</h1>{/*使用子模塊*/}<div>d:{store.moduleMobx.d}</div><div>e:{store.moduleMobx.e}</div><div>計算屬性d*e:{store.moduleMobx.dxe}</div><button onClick={()=>{store.moduleMobx.setD(20)}}>修改</button><hr/>{/*沒有使用子模塊*/}<div>a:{store.a}</div><div>b:{store.b}</div><div>計算屬性c:{store.c}</div><button onClick={()=>{store.setA(18);}}>修改</button></div>}export default observer(Notice);
總結:
1)store文件中引入:import { makeAutoObservable } from "mobx" //可觀測的2)constructor() {makeAutoObservable(this); //統一所有的this指向的固定寫法}3)實例化store,并導出store4)需要使用的組件引入store5)引入observer:import {observer} from 'mobx-react' //被它包裹的組件就是響應式的,改了數據頁面就會更新6)將observer包裹當前組件:export default observer(Notice)7)頁面使用:<div>{store.a}</div>
計算屬性:
類似 Vuex 的 getter(c值:c=a+b,當a或b發生變化的時候,我希望c同步變化,就需要用到計算屬性)在Mobx中添加計算屬性通過get關鍵詞實現:get 方法名(){return 干啥;}
模塊化:
export default { //直接導出component.js需要使用的數據數據}import moduleMobx from './moduleMobx' //引入子模塊moduleMobx, //組件使用時:store.子模塊名.d
Redux
參考網址:
Redux :https://cn.redux.js.org/introduction/getting-started/Redux Toolkit :https://toolkit.redux.js.cn/api/createSliceReact Redux :https://react-redux.js.org/tutorials/quick-start
特點:
1) 單一數據源:整個應用的狀態存儲在一個單一的對象樹,且該對象樹只存儲在一個store中2) State是只讀的:狀態是不可直接修改的,改變內部狀態的唯一方法是dispatch一個action3) 使用純函數來完成狀態變更:通過reducer將舊state和actions聯系在一起,并返回一個新的state,不產生任何副作用
安裝依賴:
npm i @reduxjs/toolkit react-redux //@reduxjs/toolkit 是 Redux Toolkit 的核心庫,包含了創建 Redux 狀態管理邏輯的工具
//react-redux 是連接 React 和 Redux 的橋梁
創建 Redux Store:
store\index.js
configureStore 是 @reduxjs/toolkit 提供的函數,自動集成了 Redux DevTools 和一些默認的中間件(如 thunk)。reducer 是狀態管理的核心
//Redux狀態管理(使用的是:Redux Toolkit+ react-redux)
import {configureStore} from '@reduxjs/toolkit';
import counterReducer from './counterSlice'; //可以創建多個slice切片(類似于模塊化)
import noticeReducer from './noticeSlice'//配置 store
const store=configureStore({reducer:{//注冊子切片(將reducer添加到store中)counter:counterReducer,notice:noticeReducer,}
})export default store;
創建 Slice:
store\counterSlice.js
Slice 是 Redux Toolkit 中的概念,集成了 action creators 和 reducer 的定義,便于模塊化管理。每個 slice 通常代表應用中的一部分狀態
createSlice 自動生成 action types 和 action creators。狀態更新使用 Immer 實現,可以直接“修改” state,而無需手動展開對象
import {createSlice} from "@reduxjs/toolkit";//定義一個counter slice (slice:切片)
const counterSlice=createSlice({name: "counter", //slice的名稱(模塊名稱獨一無二)//初始化stateinitialState: {value:25, //初始狀態},//修改數據的同步方法reducers:{addNumber:(state)=>{state.value += 1; //直接修改state(內置了Immer,無需手動返回新狀態)},reduceNumber:(state)=>{state.value -= 1;},numberByAmount:(state,action)=>{state.value += action.payload; //action.payload是傳遞的參數}}
})//導出 action creators(解構出動作創作者函數)
export const {addNumber,reduceNumber,numberByAmount} = counterSlice.actions;//導出 reducer
export default counterSlice.reducer;
將 Store 接入 React:
src\index.js
在項目的入口文件(比如 src/index.js 或 src/main.js)中,使用 react-redux 的 Provider 將 Store 提供給整個應用
import React from 'react';
import ReactDOM from 'react-dom/client';
// 導入store
import store from './store';
// 導入store提供組件Provider
import { Provider } from 'react-redux';
import './index.css';
import App from './App';
import {HashRouter} from "react-router-dom";const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Provider store={store}> {/*包裹起來,提供store數據*/}<HashRouter><App /></HashRouter></Provider>);
在組件中使用 Redux:
src\pages\home\notice\index.js
在 React 組件中,可以通過 react-redux 提供的 Hooks(如 useSelector 和 useDispatch)來訪問狀態和分發 action。useSelector 用于從 Store 中讀取狀態,useDispatch 用于觸發 action
。性能優化:如果你的組件因狀態變化頻繁重渲染,可以結合 useSelector 的第二個參數(比較函數)或 Redux Toolkit 的 createSelector 來優化選擇器
import {useSelector,useDispatch} from "react-redux";
import {addNumber,reduceNumber,numberByAmount} from "../../../store/counterSlice";
import {fetchData,twoAsync} from "../../../store/noticeSlice";const Notice = () => {//獲取狀態const count=useSelector(state => state.counter.value);const adminData=useSelector(state=>state.notice.adminDate)const status=useSelector(state=>state.notice.status);//獲取dispatch函數const dispatch=useDispatch();return <div><h1>Notice</h1><h1>計數器:{count}</h1><button onClick={()=>dispatch(addNumber())}>加 1</button><button onClick={()=>dispatch(reduceNumber())}>減 1</button><button onClick={()=>dispatch(numberByAmount(3))}>加 3</button><h1>異步處理</h1><buttononClick={()=>dispatch(fetchData())}disabled={status==="loading"}style={{color: status==="failed"?'red':'green'}}>異步fetchData</button>{JSON.stringify(adminData)}<button onClick={()=>dispatch(twoAsync({age:25,name:"lmy"}))}>異步2</button></div>}export default Notice;
異步操作:
store\noticeSlice.js
如果需要處理異步邏輯,可以使用 @reduxjs/toolkit 內置的 createAsyncThunk,然后在 createSlice 中通過 extraReducers 處理異步狀態
createAsyncThunk: 該函數有兩個參數,一個字符串,用作生成的action types的前綴;一個payload creator回調函數,應該返回一個Promise。這通常使用async/await語法編寫,因為async函數會自動返回一個Promise。createAsyncThunk函數生成一個 pending/fulfilled/rejected 基于該Promise分派動作類型的thunk
。默認redux不能在reducers中處理異步,而在外部處理或者使用自帶的一個方法createAsyncThunk;createAsyncThunk可以被認為是一個action,只不過它是異步的。進而正常通過dispatch派發即可;但是提到異步,就免不了有狀態產生(pending/fulfilled/rejected),所以結果并不能被reducers正常歸納處理
import http from "../utils/http"; //使用自己封裝的fetch
import {createSlice,createAsyncThunk} from "@reduxjs/toolkit";//1.定義異步action
export const fetchData=createAsyncThunk("notice/fetchData",async ()=>{const response=await http({url:"/admin/getadmin"}).then(res => {console.log(res);return res;})await new Promise(resolve => setTimeout(resolve, 2000)); //等待 2 秒return response.data;})//3.多個異步
export const twoAsync=createAsyncThunk("notice/twoAsync",async (obj)=>{await new Promise(resolve => setTimeout(resolve,2000));return obj;})const noticeSlice=createSlice({name: "notice",initialState: {adminDate: undefined,status:"idle",error: null,},reducers: {// ... 同步 reducers},//2.然后在createSlice中處理異步的reducer,通過extraReducers處理異步狀態extraReducers:(builder)=>{builder.addCase(fetchData.pending, (state)=>{ //action被發出,但是還沒有最終的結果state.status="loading";console.log("待定pending")}).addCase(fetchData.fulfilled, (state, action)=>{ //獲取到最終的結果(有返回值的結果)state.adminDate=action.payload;console.log(state.adminDate)state.status="succeeded";console.log("已完成fulfilled")}).addCase(fetchData.rejected, (state, action)=>{ //執行過程中有錯誤或者拋出了異常state.status='failed';state.error = action.error.message; console.log("已拒絕rejected:",state.error) })//4.多個異步.addCase(twoAsync.fulfilled, (state, {payload})=>{console.log(payload)})}
})export default noticeSlice.reducer;
參考:https://blog.csdn.net/qq_34645412/article/details/144982492
React 18 與 Redux 的結合主要依賴 @reduxjs/toolkit 和 react-redux。通過 configureStore 創建 store,用 Provider 提供給組件樹,然后在組件中使用 hooks(如 useSelector 和 useDispatch)來操作狀態
。通過以上步驟,就可以在 React 項目中成功使用 @reduxjs/toolkit 來管理狀態了 (注意:Redux Toolkit 本身與 React 版本無關,React-Redux 對 React 通常會有自己的版本要求
)
在Redux Toolkit出現之前,開發Redux應用通常涉及以下幾個步驟:
狀態管理目錄:
步驟:(建議使用 Redux Toolkit 來編寫代碼,因為它能簡化你的代碼的同時消除許多常見的 Redux 問題和 Bug)
1.定義 Action Types:在 Redux 中,所有的 action 類型需要事先定義,以便在創建 action 和 reducer 時引用//constants.js2.創建 Actions:使用定義好的 action types 創建 action 對象//actionCreators.js 3.編寫 Reducers:根據 action 的類型更新 state//reducer.js4.引入并統一導出reducer 和 action//index.js 5.創建 Store:創建 Redux store,將 counter 和 home 的 reducer 進行合并成一個 reducer//store\index.js //參考:https://blog.csdn.net/kouzuhuai2956/article/details/145001802
redux-devtools 瀏覽器工具
給瀏覽器安裝拓展:
按F12使用:可以看到存儲在 redux 中的數據