React系列:第三回(状态管理)
本文见到那介绍下React中的状态管理
适配react的状态库很多,本文涉及三个: Zustand、Redux 和 Context
Zustand
// npm install zustand
// 2. 创建store
import { create } from 'zustand';
const useCounterStore = create((set) => ({
count: 0,
// 同步更新
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
// 异步更新(内置支持,无需中间件)
incrementAsync: () => {
return new Promise((resolve) => {
setTimeout(() => {
set((state) => ({ count: state.count + 1 }));
resolve();
}, 1000);
});
}
}));
// 3. 组件中直接使用(无需Provider)
function Counter() {
// 方式1:选择单个状态(自动订阅更新)
const count = useCounterStore((state) => state.count);
const increment = useCounterStore((state) => state.increment);
// 方式2:批量选择(减少重渲染)
// const { count, increment, decrement } = useCounterStore(
// (state) => ({ count: state.count, increment: state.increment, decrement: state.decrement }),
// { shallow: true } // 浅比较,避免不必要的重渲染
// );
return (
<div>
<p>{count}</p>
<button onClick={increment}>+</button>
<button onClick={() => useCounterStore.getState().decrement()}>-</button>
<button onClick={() => useCounterStore.getState().incrementAsync()}>+(异步)</button>
</div>
);
}
Redux
// 安装
cnpm install @reduxjs/toolkit react-redux
基本使用
redux是一个状态管理容器,类似vuex,几个重要的角色: store(数据源)、action及reducer(负责修改数据)。下面代码演示他的基本使用:
// index.js中引入redux的store和provider方法
import store from './store'
import { Provider } from 'react-redux'
...
...
<Provider store={store}>
<RouterProvider router={router}></RouterProvider>
</Provider>
// store的index
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from './mouduls/counterStoreA'
const store = configureStore({
reducer: {
counter: counterReducer,
}
})
export default store
// counterReducer文件
import { createSlice } from "@reduxjs/toolkit";
import axios from 'axios';
const counterStore = createSlice({
name: 'counter',
initialState: {
count: 0
},
reducers: {
increment(state, value) {
state.count = state.count + value.payload
},
decrement(state, value) {
state.count = state.count - value.payload
}
}
})
const { increment, decrement } = counterStore.actions
const reducer = counterStore.reducer
export { increment, decrement}
export default reducer
至此,我们创建了一个redux中的变量count,下面在组件中使用。
import { useDispatch, useSelector } from 'react-redux'
import { decrement, increment } from '../../store/mouduls/counterStoreA';
...
...
const HomeComponent = () => {
const { count } = useSelector((state: any) => state.counter)
const dispatch = useDispatch()
console.log('count:', count)
return (
<div>
<div>我是HomeComponent...</div>
<Button onClick={() => dispatch(decrement(10))}>减减</Button>
<Button onClick={() => dispatch(increment(10))}>加加</Button>
<span>{count}</span>
</div>
)
}
通过useSelector获取变量,通过useDispatch提交修改,效果如下:
异步获取更新值
通过异步请求拿到数据,更新store中的变量。
// 异步请求
// store/counterSlice.js
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import { getInfo } from "@/server/testpageAPI";
export const fetchCounterAsync: any = createAsyncThunk(
"counter/fetchCounter",
async () => {
const response = await getInfo();
return await response;
}
);
const counterSlice = createSlice({
name: "counter",
initialState: {
value: 0,
status: "idle"
},
reducers: {
increment: (state, action) => {
state.value = action.payload; // 同步操作
},
decrement: (state, action) => {
state.value = action.payload; // 同步操作
}
},
extraReducers: builder => {
builder
.addCase(fetchCounterAsync.pending, state => {
state.status = "loading";
})
.addCase(fetchCounterAsync.fulfilled, (state, action) => {
state.status = "succeeded";
state.value = action.payload.data.word;
})
.addCase(fetchCounterAsync.rejected, state => {
state.status = "failed";
});
}
});
export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;
// store/index文件
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./counterSlice";
export const store = configureStore({
reducer: {
counter: counterReducer
}
});
// 组件中使用
export const SiderComponent = () => {
const dispatch = useDispatch();
const counter = useSelector((state: any) => state.counter.value);
const status = useSelector((state: any) => state.counter.status);
return (
<div className="w-full h-full ">
<div className="w-full">
{counter}
</div>
<div className="w-full">
{status}
</div>
<ButtonCommon
className="w-full"
type={EButtonType.SIMPLE}
onClick={() => {
const newVal = counter + 1;
dispatch(increment(newVal));
}}
>
<span className="">+</span>
</ButtonCommon>
<ButtonCommon
className="w-full"
type={EButtonType.SIMPLE}
onClick={() => {
const newVal = counter - 1;
dispatch(decrement(newVal));
}}
>
<span className="">-</span>
</ButtonCommon>
<ButtonCommon
className="w-full"
type={EButtonType.SIMPLE}
onClick={() => {
dispatch(fetchCounterAsync());
}}
>
<span className="">异步</span>
</ButtonCommon>
</div>
);
};
总结一下
action --> dispatcher ---> store ---> view, 同vuex如出一辙
Content provider
没啥说的,直接上代码
// 定义一个context文件,暴露组件及context
import { createContext, useEffect, useState } from "react";
interface ColorContext {
color: any;
setColor: React.Dispatch<React.SetStateAction<any>>;
}
export const ColorContext = createContext({} as ColorContext);
export default function ColorProvider(props: any) {
const [color, setColor] = useState("#000000");
useEffect(
() => {
console.log("color>>>", color);
},
[color]
);
return (
<ColorContext.Provider
value={{
color,
setColor
}}
>
{props.children}
</ColorContext.Provider>
);
}
// 导入ColorProvider,包裹子组件
<ColorProvider>
<div
className={`w-full h-full label px-[10px] py-[10px] ${pattern.flexbet}`}
>
...
</div>
</ColorProvider>
<!-- 消费 -->
import {ColorContext} from '../Layout'
console.log(useContext(ColorContext))
各自弊端
- Context Provider
性能失控。状态变更会触发所有消费 useContext 的组件无条件重渲染,且无内置性能优化方案,手动优化(拆分 Context + memo/useMemo)成本高、易出错。 - Redux
开发效率低。模板代码冗余、学习 / 使用成本高,即使简单的状态更新也需定义 Action、Reducer、Action Creator 等多层结构,中小型项目中 “过度工程化”,开发效率低。 - Zustand
团队协作可维护性弱。缺乏强规范约束,多人协作时易出现状态修改逻辑混乱(如随意修改 Store 状态、异步逻辑散落在各处),且生态和工具链远不如 Redux 成熟,复杂场景(如多请求依赖、状态回溯)需手动封装。
总体:这三个目前算是主流吧,当然还有很多其他的选项。从实操层面看,无脑zustand即可。
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
