马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
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 API
- try {
- 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 等工具。
- 替代方案:手动编写 action、reducer 和中心件设置(传统 Redux)。
六、Redux 适用场景与替代方案
1. 何时使用 Redux?
- 多个组件需要共享同一状态。
- 状态更新逻辑复杂(如跨组件联动)。
- 需要时间旅行调试、持久化状态或记录状态历史。
2. 轻量替代方案
方案特点Context APIReact 内置,适合简朴状态共享,但缺乏中心件、性能优化工具。RecoilFacebook 实验性状态管理库,原子化状态设计,适合复杂数据流。Zustand轻量级,基于 Hook 的状态管理,API 简洁。 七、总结
- Redux 焦点:Store、Action、Reducer、Middleware。
- React-Redux 集成:Provider、useSelector、useDispatch。
- 异步处理:通过 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 确保范例安全。
- 公道使用选择器和中心件优化性能。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |