【React】状态管理 Context API \ MobX \ Redux

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

相关推荐
爱分享的程序员1 小时前
全栈架构设计图
前端
KenXu1 小时前
YYEVA WebGPU 渲染实现技术解析
前端·webgl
~卷心菜~1 小时前
CSS基础-即学即用 -- 笔记1
前端·css·笔记
我最厉害。,。1 小时前
CSRF 请求伪造&Referer 同源&置空&配合 XSS&Token 值校验&复用删除
前端·xss·csrf
梦魇泪1 小时前
实践项目开发-hbmV4V20250407-跨平台开发框架深度解析与VSCode一站式开发实践
vscode·react.js·taro
vvilkim1 小时前
深入解析React.lazy与Suspense:现代React应用的性能优化利器
前端·react.js·前端框架
野猪亨利2581 小时前
【JS 小白也能懂】回调函数:让代码学会「听话办事」的魔法
前端
五号厂房1 小时前
前端接口编写的最佳实践总结:设计、实现、维护全流程
前端
Cutey9161 小时前
Vue 实现多语言国际化的完整指南
前端·javascript·面试
广龙宇1 小时前
【Web API系列】Web Shared Storage API 深度解析:WindowSharedStorage 接口实战指南
前端