React有自己状态管理,周边生态也有很多状态管理
Context API
直接从react中引入并调用即可,context包含两个东西:
javascript
Provider:提供商(翻译),提供数据;属性:value={提供商需要提供的数据}
<Provider value={}>
组件内容
</Provider>
Consumer:消费者(翻译),使用数据;内部是函数
<Consumer>
{
(value)=>{ return 组件内容 }
}
</Consumer>
测试Context:
javascript
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
javascript
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
javascript
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
javascript
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;
总结:
javascript
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分两部分:
html
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只支持函数组件,体积较小,
可以根据具体情况进行下载
html
npm i mobx mobx-react --save
语法参考:<!-- https://mobx.js.org/README.html -->和<!-- https://github.com/mobxjs/mobx -->
引入:
javascript
import { makeAutoObservable } from "mobx" //makeAutoObservable:使自动可观察
import { observer } from "mobx-react-lite" //observer:观察者
使用mobx,创建store类,并实例化导出以供使用:src\store\index.js(class写法)
javascript
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的函数组件的写法:
javascript
import { makeAutoObservable } from "mobx"
import moduleMobx from './moduleMobx' //引入子模块
const store=()=>{
return makeAutoObservable({
//...moduleMobx //在组件上使用数据的时候写法就是:store.d
moduleMobx, //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
javascript
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
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);
总结:
javascript
1)store文件中引入:import { makeAutoObservable } from "mobx" //可观测的
2)constructor() {
makeAutoObservable(this); //统一所有的this指向的固定写法
}
3)实例化store,并导出store
4)需要使用的组件引入store
5)引入observer:import {observer} from 'mobx-react' //被它包裹的组件就是响应式的,改了数据页面就会更新
6)将observer包裹当前组件:export default observer(Notice)
7)页面使用:<div>{store.a}</div>
计算属性:
javascript
类似 Vuex 的 getter(c值:c=a+b,当a或b发生变化的时候,我希望c同步变化,就需要用到计算属性)
在Mobx中添加计算属性通过get关键词实现:
get 方法名(){
return 干啥;
}
模块化:
javascript
export default { //直接导出component.js需要使用的数据
数据
}
import moduleMobx from './moduleMobx' //引入子模块
moduleMobx, //组件使用时:store.子模块名.d
Redux
参考网址:
javascript
Redux :https://cn.redux.js.org/introduction/getting-started/
Redux Toolkit :https://toolkit.redux.js.cn/api/createSlice
React Redux :https://react-redux.js.org/tutorials/quick-start
特点:
javascript
1) 单一数据源:整个应用的状态存储在一个单一的对象树,且该对象树只存储在一个store中
2) State是只读的:状态是不可直接修改的,改变内部状态的唯一方法是dispatch一个action
3) 使用纯函数来完成状态变更:通过reducer将旧state和actions联系在一起,并返回一个新的state,不产生任何副作用

安装依赖:
javascript
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 是状态管理的核心
javascript
//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,而无需手动展开对象
javascript
import {createSlice} from "@reduxjs/toolkit";
//定义一个counter slice (slice:切片)
const counterSlice=createSlice({
name: "counter", //slice的名称(模块名称独一无二)
//初始化state
initialState: {
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 提供给整个应用
javascript
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 来优化选择器
javascript
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>
<button
onClick={()=>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正常归纳处理
javascript
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)
javascript
1.定义 Action Types:在 Redux 中,所有的 action 类型需要事先定义,以便在创建 action 和 reducer 时引用
//constants.js
2.创建 Actions:使用定义好的 action types 创建 action 对象
//actionCreators.js
3.编写 Reducers:根据 action 的类型更新 state
//reducer.js
4.引入并统一导出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 中的数据
