React 状态管理:Redux Toolkit 快速上手指南

83 阅读6分钟

React 状态管理:Redux Toolkit 快速上手指南

🤔 为什么需要 Redux Toolkit?

Redux 是 React 生态中最流行的状态管理库之一,但传统的 Redux 开发存在以下问题:

  • 样板代码过多,开发效率低
  • 配置复杂,需要多个依赖(如 Redux DevTools、Thunk 等)
  • 容易出错,需要手动处理不可变更新
  • 类型定义复杂,TypeScript 支持不够友好

Redux Toolkit(简称 RTK)就是为了解决这些问题而生的!它是 Redux 官方推荐的现代化 Redux 开发方式,具有以下特点:

  • 📦 开箱即用:集成了 Redux 核心、Thunk、DevTools 等常用工具
  • 🎯 减少样板代码:提供 createSlice 等 API,自动生成 action 和 reducer
  • 🔄 不可变更新:内置 Immer 库,支持直接修改状态的写法
  • 异步支持:内置 createAsyncThunk,轻松处理异步逻辑
  • 📝 类型安全:更好的 TypeScript 支持
  • 🔧 灵活配置:支持自定义中间件和扩展

💡 Redux Toolkit 核心概念

在开始使用 Redux Toolkit 之前,我们需要了解以下核心概念:

  1. Store:全局状态容器,整个应用只有一个 Store
  2. Slice:状态的切片,包含 reducer 和 action
  3. Action:描述状态变化的对象
  4. Reducer:处理状态变化的函数
  5. Selector:从状态中提取数据的函数
  6. Thunk:处理异步逻辑的中间件

🚀 Redux Toolkit 基础实现

1. 安装 Redux Toolkit

npm install @reduxjs/toolkit react-redux
# 或
yarn add @reduxjs/toolkit react-redux
# 或
pnpm add @reduxjs/toolkit react-redux

2. 创建 Slice

使用 createSlice 创建一个状态切片:

import { createSlice } from '@reduxjs/toolkit';

// 初始状态
const initialState = {
  count: 0,
  status: 'idle',
  error: null,
};

// 创建计数器 slice
const counterSlice = createSlice({
  name: 'counter', // slice 名称
  initialState, // 初始状态
  reducers: {
    // 同步 action
    increment: (state) => {
      // 使用 Immer,直接修改状态
      state.count += 1;
    },
    decrement: (state) => {
      state.count -= 1;
    },
    incrementByAmount: (state, action) => {
      // action.payload 是传入的参数
      state.count += action.payload;
    },
    reset: (state) => {
      state.count = 0;
    },
  },
});

// 导出 action creators
export const { increment, decrement, incrementByAmount, reset } = counterSlice.actions;

// 导出 reducer
export default counterSlice.reducer;

3. 配置 Store

使用 configureStore 创建并配置 Redux Store:

import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './features/counter/counterSlice';

// 配置 store
const store = configureStore({
  reducer: {
    // 将 slice reducer 添加到 store 中
    counter: counterReducer,
    // 可以添加多个 slice reducer
    // user: userReducer,
    // product: productReducer,
  },
  // 可以添加自定义中间件
  // middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
  // 开启 DevTools(默认开启)
  devTools: process.env.NODE_ENV !== 'production',
});

// 导出 store
export default store;

// 导出 RootState 和 AppDispatch 类型(用于 TypeScript)
export type RootState = ReturnType;
export type AppDispatch = typeof store.dispatch;

4. 提供 Store

使用 Provider 组件将 Store 提供给整个应用:

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { Provider } from 'react-redux';
import store from './app/store';
import './index.css';

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
  
    
      
    
  
);

5. 在组件中使用

使用 useSelectoruseDispatch 在组件中访问和更新状态:

import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement, incrementByAmount, reset } from './features/counter/counterSlice';
import { RootState } from './app/store';

const Counter = () => {
  // 获取状态
  const count = useSelector((state: RootState) => state.counter.count);
  // 获取 dispatch 函数
  const dispatch = useDispatch();

  return (
    <div>
      <h2>当前计数: {count}</h2>
      <div>
         dispatch(increment())}>+
         dispatch(decrement())}>-
         dispatch(reset())}>重置
         dispatch(incrementByAmount(5))}>+5
      </div>
    </div>
  );
};

export default Counter;

🎯 Redux Toolkit 进阶用法

1. 异步逻辑处理

使用 createAsyncThunk 处理异步逻辑:

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

// 定义异步 thunk
export const fetchUsers = createAsyncThunk(
  'user/fetchUsers', // thunk 名称
  async () => {
    // 异步请求
    const response = await fetch('https://jsonplaceholder.typicode.com/users');
    return response.json();
  }
);

// 初始状态
const initialState = {
  users: [],
  status: 'idle', // idle | loading | succeeded | failed
  error: null,
};

// 创建用户 slice
const userSlice = createSlice({
  name: 'user',
  initialState,
  reducers: {},
  // 处理异步 action
  extraReducers: (builder) => {
    builder
      // 处理 pending 状态
      .addCase(fetchUsers.pending, (state) => {
        state.status = 'loading';
      })
      // 处理 fulfilled 状态
      .addCase(fetchUsers.fulfilled, (state, action) => {
        state.status = 'succeeded';
        // 将获取的数据添加到状态中
        state.users = action.payload;
      })
      // 处理 rejected 状态
      .addCase(fetchUsers.rejected, (state, action) => {
        state.status = 'failed';
        // 保存错误信息
        state.error = action.error.message || null;
      });
  },
});

export default userSlice.reducer;

在组件中使用:

import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchUsers } from './features/user/userSlice';
import { RootState } from './app/store';

const UserList = () => {
  const { users, status, error } = useSelector((state: RootState) => state.user);
  const dispatch = useDispatch();

  useEffect(() => {
    // 触发异步 action
    dispatch(fetchUsers());
  }, [dispatch]);

  if (status === 'loading') {
    return <div>加载中...</div>;
  }

  if (status === 'failed') {
    return <div>错误: {error}</div>;
  }

  return (
    <div>
      <h2>用户列表</h2>
      <ul>
        {users.map((user) => (
          <li>
            <h3>{user.name}</h3>
            <p>{user.email}</p>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default UserList;

2. 创建自定义 Hook

为了方便使用,可以创建自定义 Hook:

import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import { RootState, AppDispatch } from './app/store';

// 自定义 useDispatch Hook,带有正确的类型
export const useAppDispatch = () => useDispatch();

// 自定义 useSelector Hook,带有正确的类型
export const useAppSelector: TypedUseSelectorHook = useSelector;

在组件中使用:

import React from 'react';
import { useAppSelector, useAppDispatch } from './app/hooks';
import { increment, decrement } from './features/counter/counterSlice';

const Counter = () => {
  const count = useAppSelector((state) => state.counter.count);
  const dispatch = useAppDispatch();

  return (
    <div>
      <p>计数: {count}</p>
       dispatch(increment())}>增加
       dispatch(decrement())}>减少
    </div>
  );
};

3. 模块化 Slice

对于大型应用,可以将不同功能的状态拆分为多个 Slice:

// app/store.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';
import userReducer from '../features/user/userSlice';
import productReducer from '../features/product/productSlice';

const store = configureStore({
  reducer: {
    counter: counterReducer,
    user: userReducer,
    product: productReducer,
  },
});

export default store;

4. 使用 createEntityAdapter 管理实体

createEntityAdapter 可以帮助我们更方便地管理实体数据(如用户列表、产品列表等):

import { createSlice, createAsyncThunk, createEntityAdapter } from '@reduxjs/toolkit';

// 创建实体适配器
const usersAdapter = createEntityAdapter({
  // 可以指定主键,默认为 'id'
  selectId: (user) => user.id,
  // 可以指定排序函数
  sortComparer: (a, b) => a.name.localeCompare(b.name),
});

// 定义异步 thunk
export const fetchUsers = createAsyncThunk('user/fetchUsers', async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/users');
  return response.json();
});

// 初始状态
const initialState = usersAdapter.getInitialState({
  status: 'idle',
  error: null,
});

// 创建用户 slice
const userSlice = createSlice({
  name: 'user',
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchUsers.pending, (state) => {
        state.status = 'loading';
      })
      .addCase(fetchUsers.fulfilled, (state, action) => {
        state.status = 'succeeded';
        // 使用适配器的 upsertMany 方法更新状态
        usersAdapter.upsertMany(state, action.payload);
      })
      .addCase(fetchUsers.rejected, (state, action) => {
        state.status = 'failed';
        state.error = action.error.message || null;
      });
  },
});

// 导出选择器
export const {
  selectAll: selectAllUsers,
  selectById: selectUserById,
  selectIds: selectUserIds,
  selectEntities: selectUserEntities,
  selectTotal: selectTotalUsers,
} = usersAdapter.getSelectors((state) => state.user);

export default userSlice.reducer;

在组件中使用:

import React, { useEffect } from 'react';
import { useAppSelector, useAppDispatch } from './app/hooks';
import { fetchUsers, selectAllUsers } from './features/user/userSlice';

const UserList = () => {
  const users = useAppSelector(selectAllUsers);
  const { status, error } = useAppSelector((state) => state.user);
  const dispatch = useAppDispatch();

  useEffect(() => {
    if (status === 'idle') {
      dispatch(fetchUsers());
    }
  }, [status, dispatch]);

  // 渲染逻辑...
};

📝 Redux Toolkit 最佳实践

  1. 使用 createSlice:优先使用 createSlice 而不是手动创建 action 和 reducer
  2. 保持 Slice 独立:每个 Slice 只负责管理相关的状态
  3. 使用自定义 Hook:创建 useAppDispatchuseAppSelector 等自定义 Hook,提高开发效率
  4. 处理异步错误:在 createAsyncThunk 中处理错误,并在 extraReducers 中更新状态
  5. 使用 Immer 优势:利用 Immer 库,直接修改状态的写法,减少不可变更新的错误
  6. TypeScript 支持:为 State、Action 等添加类型定义,提高代码质量
  7. 合理使用选择器:使用选择器从状态中提取数据,便于缓存和性能优化
  8. DevTools 调试:使用 Redux DevTools 调试状态变化

🎯 Redux Toolkit 适用场景

Redux Toolkit 适用于以下场景:

  • 中大型 React 应用
  • 需要复杂状态管理的项目
  • 团队协作开发的项目
  • 需要严格的状态管理流程的项目
  • 希望使用官方推荐的 Redux 开发方式的项目

🔧 Redux Toolkit 与其他状态管理库的比较

特性Redux ToolkitZustandJotai
大小~6KB~1KB~2KB
学习曲线
Provider 需要
异步支持内置内置内置
DevTools 支持
不可变更新是(Immer)
类型安全
社区支持

🚀 总结

Redux Toolkit 是 Redux 官方推荐的现代化 Redux 开发方式,它通过提供一系列简洁的 API,大大减少了 Redux 的样板代码,提高了开发效率。无论是新的 Redux 项目还是现有的 Redux 项目迁移,Redux Toolkit 都是一个很好的选择。

通过本文的介绍,相信你已经对 Redux Toolkit 有了基本的了解,包括它的核心概念、基础用法和进阶功能。如果你正在使用 Redux 或者考虑使用状态管理库,那么 Redux Toolkit 绝对值得一试!

至此,我们已经介绍了三种流行的 React 状态管理库:Zustand、Jotai 和 Redux Toolkit。每种库都有其特点和适用场景,你可以根据项目需求和团队偏好选择合适的状态管理方案。

希望本文对你有所帮助,祝你在 React 开发中取得更好的成果!✨