- 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 中的数据