当前位置: 首页 > web >正文

React Redux 与 Zustand

Redux

一、Redux 核心概念

1. 为什么需要 Redux?

  • 解决的问题:在大型 React 应用中,跨组件共享状态、管理复杂数据流。

  • 优势

    • 单一数据源:全局状态集中存储在 Store 中。

    • 可预测性:通过严格的规则(纯函数、不可变性)管理状态变化。

    • 调试友好:支持时间旅行调试(Redux DevTools)。

    • 中间件支持:处理异步逻辑(如 API 调用)。


2. Redux 三大原则

原则说明
单一数据源整个应用的状态存储在唯一的 Store 对象树中。
状态只读只能通过 dispatch(action) 修改状态,禁止直接修改。
使用纯函数修改状态通过 Reducer 函数接收旧状态和 Action,返回新状态(无副作用)。

3. 核心概念

概念作用
Store全局状态容器,通过 createStore 创建。
Action描述状态变化的普通对象,必须包含 type 字段。
Reducer纯函数,接收当前 state 和 action,返回新的 state
Dispatch触发状态更新的方法,store.dispatch(action)
Middleware扩展 Redux 功能(如处理异步操作),位于 dispatch 和 Reducer 之间。

二、Redux 与 React 集成(React-Redux)

1. 安装依赖

npm install redux react-redux @reduxjs/toolkit

2. 核心 API

  • Provider:包裹根组件,将 Store 传递给子组件。

  • useSelector:从 Store 中读取状态(替代 mapStateToProps)。

  • useDispatch:获取 dispatch 方法(替代 mapDispatchToProps)。


三、Redux 使用步骤(代码示例)

1. 定义 Reducer 和 Action

// src/store/counterSlice.js(使用 Redux Toolkit)
import { createSlice } from '@reduxjs/toolkit';const counterSlice = createSlice({name: 'counter',initialState: { value: 0 },reducers: {increment: (state) => {state.value += 1; // Redux Toolkit 允许直接修改(内部使用 Immer)},decrement: (state) => {state.value -= 1;},incrementByAmount: (state, action) => {state.value += action.payload;},},
});export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;

2. 创建 Store

// src/store/index.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';export const store = configureStore({reducer: {counter: counterReducer,},
});

3. 将 Store 注入 React 应用

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import { store } from './store';ReactDOM.render(<Provider store={store}><App /></Provider>,document.getElementById('root')
);

4. 在组件中访问状态和触发 Action

// src/components/Counter.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement, incrementByAmount } from '../store/counterSlice';function Counter() {const count = useSelector((state) => state.counter.value);const dispatch = useDispatch();return (<div><button onClick={() => dispatch(decrement())}>-</button><span>{count}</span><button onClick={() => dispatch(increment())}>+</button><button onClick={() => dispatch(incrementByAmount(5))}>+5</button></div>);
}export default Counter;

四、异步操作与中间件

1. 使用 Redux Thunk(处理异步逻辑)

// src/store/userSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';// 定义异步 Thunk
export const fetchUser = createAsyncThunk('user/fetchUser', async (userId) => {const response = await axios.get(`https://api.example.com/users/${userId}`);return response.data;
});const userSlice = createSlice({name: 'user',initialState: { data: null, loading: false, error: null },extraReducers: (builder) => {builder.addCase(fetchUser.pending, (state) => {state.loading = true;}).addCase(fetchUser.fulfilled, (state, action) => {state.loading = false;state.data = action.payload;}).addCase(fetchUser.rejected, (state, action) => {state.loading = false;state.error = action.error.message;});},
});export default userSlice.reducer;

2. 组件中调用异步 Action

function UserProfile({ userId }) {const dispatch = useDispatch();const { data, loading, error } = useSelector((state) => state.user);useEffect(() => {dispatch(fetchUser(userId));}, [dispatch, userId]);if (loading) return <div>Loading...</div>;if (error) return <div>Error: {error}</div>;return <div>Username: {data.name}</div>;
}

Redux Thunk 完整使用流程:用户数据请求


1. 创建异步 Thunk

// src/store/userSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';// 创建异步 Thunk Action
export const fetchUser = createAsyncThunk('user/fetchUser', // 唯一标识符(推荐格式:"slice名称/action名称")async (userId, thunkAPI) => { // 接收参数和 Thunk APItry {const response = await axios.get(`https://api.example.com/users/${userId}`);return response.data; // 成功时返回数据作为 payload} catch (error) {return thunkAPI.rejectWithValue(error.message); // 失败时传递错误信息}}
);

关键点:

  • createAsyncThunk 会自动生成三种 action 类型:

    • user/fetchUser/pending(请求开始)

    • user/fetchUser/fulfilled(请求成功)

    • user/fetchUser/rejected(请求失败)

  • 参数说明:

    • 第一个参数:唯一标识符(建议用 slice名称/action名称 格式)

    • 第二个参数:异步处理函数(可接收参数和 thunkAPI 对象)


2. 创建 Slice 处理状态

const userSlice = createSlice({name: 'user', // slice 名称initialState: { // 初始状态data: null,   // 用户数据loading: false, // 加载状态error: null    // 错误信息},extraReducers: (builder) => {builder// 处理 pending 状态(请求开始).addCase(fetchUser.pending, (state) => {state.loading = true;state.error = null; // 重置错误})// 处理 fulfilled 状态(请求成功).addCase(fetchUser.fulfilled, (state, action) => {state.loading = false;state.data = action.payload; // 存储返回数据})// 处理 rejected 状态(请求失败).addCase(fetchUser.rejected, (state, action) => {state.loading = false;state.error = action.payload || action.error.message;});}
});export default userSlice.reducer;

3. 配置 Store

// src/store/store.js
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './userSlice';export default configureStore({reducer: {user: userReducer // 注册 slice}
});

4. 在组件中使用

// src/components/UserProfile.js
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchUser } from '../store/userSlice';function UserProfile({ userId }) {const dispatch = useDispatch();const { data, loading, error } = useSelector(state => state.user);useEffect(() => {dispatch(fetchUser(userId)); // 触发异步请求}, [dispatch, userId]);if (loading) return <div>加载中...</div>;if (error) return <div>错误:{error}</div>;return (<div><h2>{data.name}</h2><p>邮箱:{data.email}</p></div>);
}export default UserProfile;

五、Redux 最佳实践

1. 项目结构

  • 推荐结构(按功能模块划分):

    src/store/slices/       // Redux Toolkit 的 Slice 文件index.js      // Store 配置components/     // UI 组件features/       // 包含业务逻辑的组件

2. 状态设计原则

  • 最小化状态:避免冗余数据,只存储必要状态。

  • 规范化数据:使用 id 作为键,避免嵌套过深(可搭配 normalizr 库)。

3. 性能优化

  • 使用 React.memo:避免不必要的组件渲染。

  • 选择精确的状态片段useSelector 尽量返回最小化数据。

    // ✅ 精确选择
    const count = useSelector((state) => state.counter.value);// ❌ 避免返回整个 state.counter
    const counter = useSelector((state) => state.counter);

4. 使用 Redux Toolkit

  • 优势:减少样板代码,内置 immer(允许直接修改状态)、createAsyncThunk 等工具。

  • 替代方案:手动编写 actionreducer 和中间件配置(传统 Redux)。


六、Redux 适用场景与替代方案

1. 何时使用 Redux?

  • 多个组件需要共享同一状态。

  • 状态更新逻辑复杂(如跨组件联动)。

  • 需要时间旅行调试、持久化状态或记录状态历史。

2. 轻量替代方案

方案特点
Context APIReact 内置,适合简单状态共享,但缺乏中间件、性能优化工具。
RecoilFacebook 实验性状态管理库,原子化状态设计,适合复杂数据流。
Zustand轻量级,基于 Hook 的状态管理,API 简洁。

七、总结

  • Redux 核心StoreActionReducerMiddleware

  • React-Redux 集成ProvideruseSelectoruseDispatch

  • 异步处理:通过 Redux Thunk 或 createAsyncThunk 管理 API 调用。

  • 最佳实践:使用 Redux Toolkit 简化代码,合理设计状态结构。


   

Zustand

一、Zustand 核心概念

1. 定位与特点

  • 轻量级状态管理:专为 React 设计,API 简洁,学习成本低。

  • 脱离组件树:状态独立于 UI 层级,可在组件外访问。

  • 高性能:按需订阅状态片段,避免不必要的渲染。

  • 中间件支持:集成持久化、Immer(不可变更新)、日志等。


二、基础使用

1. 创建 Store

// store/counterStore.ts
import { create } from 'zustand';type CounterState = {count: number;increment: () => void;decrement: () => void;
};export const useCounterStore = create<CounterState>((set) => ({count: 0,increment: () => set((state) => ({ count: state.count + 1 })),decrement: () => set((state) => ({ count: state.count - 1 })),
}));

2. 组件中使用状态

import { useCounterStore } from './store/counterStore';function Counter() {const { count, increment, decrement } = useCounterStore();return (<div><button onClick={decrement}>-</button><span>{count}</span><button onClick={increment}>+</button></div>);
}

三、高级功能

1. 状态切片与选择器(Selector)

按需订阅部分状态,避免全局重新渲染:

// 只订阅 count 值的变化
const count = useCounterStore((state) => state.count);

2. 异步操作

在 Store 中定义异步 Action:

type UserStore = {user: User | null;fetchUser: (id: string) => Promise<void>;
};export const useUserStore = create<UserStore>((set) => ({user: null,fetchUser: async (id) => {const response = await fetch(`/api/users/${id}`);const user = await response.json();set({ user });},
}));

3. 中间件(Middleware)

持久化存储(Persist)

import { persist } from 'zustand/middleware';export const useAuthStore = create(persist((set) => ({token: null,login: (token) => set({ token }),logout: () => set({ token: null }),}),{ name: 'auth-storage' } // 存储到 localStorage)
);

不可变更新(Immer)

import { immer } from 'zustand/middleware/immer';export const useTodoStore = create(immer((set) => ({todos: [],addTodo: (text) =>set((state) => {state.todos.push({ text, completed: false }); // 直接修改 draft}),}))
);

四、最佳实践

1. 合理拆分 Store

  • 按功能模块拆分:避免单一 Store 过于臃肿。

    // store/userStore.ts
    export const useUserStore = create(...);// store/cartStore.ts
    export const useCartStore = create(...);

2. 类型安全(TypeScript)

为 Store 和 Actions 定义明确类型:

type UserState = {user: User | null;setUser: (user: User) => void;clearUser: () => void;
};export const useUserStore = create<UserState>((set) => ({user: null,setUser: (user) => set({ user }),clearUser: () => set({ user: null }),
}));

3. 性能优化

  • 使用选择器:避免订阅无关状态。

  • 结合 shallow 比较:优化对象/数组类型的状态订阅。

    import { shallow } from 'zustand/shallow';const { user, setUser } = useUserStore((state) => ({ user: state.user, setUser: state.setUser }),shallow
    );

4. 在组件外访问 Store

直接调用 Store 方法(如 API 模块中):

// 在非组件代码中
import { useCounterStore } from './store/counterStore';const { increment } = useCounterStore.getState();
increment();

五、与 Redux 和 Context API 对比

特性ZustandReduxContext API
复杂度极低高(需 Action、Reducer、Middleware)
性能按需订阅状态,自动优化需手动优化(如 reselect全子树渲染,需手动优化
中间件/插件支持(持久化、Immer 等)丰富(Redux Thunk、Saga 等)
适用场景中小型应用,快速开发大型应用,需严格架构简单状态共享,低频更新
TypeScript天然支持需额外配置支持

六、常见问题与解决方案

1. Store 状态更新后组件未渲染

  • 原因:组件未订阅相关状态或选择器未正确更新。

  • 解决:检查选择器逻辑,确保状态更新触发组件重渲染。

2. 循环依赖问题

  • 场景:多个 Store 相互依赖。

  • 解决:通过 getState 在 Action 中访问其他 Store:

    // store/authStore.ts
    import { useCartStore } from './cartStore';export const useAuthStore = create((set) => ({login: (user) => {const { clearCart } = useCartStore.getState();clearCart(); // 登录时清空购物车set({ user });},
    }));

3. Zustand 与 Next.js 服务端渲染(SSR)

  • 问题:服务端与客户端状态不一致。

  • 解决:使用 persist 中间件 + 自定义存储(如 Cookie):

    import { persist, createJSONStorage } from 'zustand/middleware';export const useStore = create(persist((set) => ({ ... }),{name: 'store',storage: createJSONStorage(() => localStorage), // 或自定义存储})
    );

七、总结

  • 核心优势:简洁、高性能、脱离组件树、中间件支持。

  • 适用场景:中小型 React 应用、需快速开发、状态共享与优化。

  • 最佳实践

    • 按功能拆分 Store。

    • 使用 TypeScript 确保类型安全。

    • 合理使用选择器和中间件优化性能。


 

http://www.xdnf.cn/news/3377.html

相关文章:

  • Python10天冲刺-设计模型之策略模式
  • 定义一个3D cube,并计算cube每个顶点的像素坐标
  • Rust中避免过度使用锁导致性能问题的策略
  • 【音频】基础知识
  • Elasticsearch 根据两个字段搜索
  • Python项目源码69:Excel数据筛选器1.0(tkinter+sqlite3+pandas)
  • 约玩、搭子组局、线下约玩、助教系统源码
  • VSCode开发调试Python入门实践(Windows10)
  • HTTP知识速通
  • 计算机网络实验七:数据抓包与协议分析
  • 【STM32】ADC的认识和使用——以STM32F407为例
  • 分布式锁的几种实现
  • 使用HunyuanVideo搭建文本生视频大模型
  • OpenSSL应用实践:嵌入式数据安全实战指南
  • 使用Node编写轻量级后端快速入门
  • 极简GIT使用
  • 【内存管理】对象树(内存管理)
  • (持续更新)Ubuntu搭建LNMP(Linux + Nginx + MySQL + PHP)环境
  • DeepSeek生成Word文档的创新路径与应用
  • 【计算机视觉】三维视觉:Nerfstudio:模块化神经辐射场框架的技术突破与实战指南
  • 操作系统OS是如何指挥外围设备的呢?
  • MySQL 服务搭建
  • Leetcode刷题记录25——合并区间
  • MySQL与分布式架构的碰撞
  • 使用MGeo模型高精度实现文本中地址识别
  • 题解:洛谷 CF2091E Interesting Ratio
  • Java 中使用正则表达式
  • 在Linux中,KVM和Docker在Linux虚拟化中的区别是什么?
  • 【计算机视觉】语义分割:Mask2Former:统一分割框架的技术突破与实战指南
  • Mysql常用函数解析