React 状态管理库相关收录

React 状态管理库相关收录

本文档总结了当前主流 React 状态管理库的核心特性、优缺点及使用模式,方便快速查阅和选型。


概览与选型指南

快速选型矩阵

库名 核心思想 适用场景
Zustand 极简单一 Store 中小型项目、追求开发效率
Redux (RTK) 严格单向数据流 大型复杂应用、强可维护性
Jotai 原子化状态 复杂组件状态、细粒度控制
MobX 响应式编程 中大型项目、熟悉响应式编程
Context API 内置状态传递 低频更新的全局状态

选型考虑因素

  1. 项目规模

    • 小型项目:Zustand、Jotai、Context API
    • 大型项目:Redux Toolkit、MobX
  2. 团队熟悉度

    • 熟悉 Flux 架构:Redux
    • 偏好响应式:MobX
    • 追求简洁:Zustand、Jotai
  3. 性能要求

    • 高频更新:Zustand、Jotai、MobX
    • 低频更新:Context API
  4. TypeScript 支持

    • 优秀:Zustand、Redux Toolkit、Jotai
    • 良好:MobX

Zustand

核心特性

  • ✅ 极简 API,学习成本低
  • ✅ 无需 Provider 包裹
  • ✅ 基于 Hook 的使用方式
  • ✅ 自动不可变更新(内置 Immer)
  • ✅ 卓越的性能(精确更新)
  • ✅ 一流的 TypeScript 支持
  • ✅ 中间件生态

基本用法

javascript 复制代码
import { create } from "zustand";

// 创建 Store
const useBearStore = create((set, get) => ({
  bears: 0,
  fish: 10,
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
  removeAllBears: () => set({ bears: 0 }),
  eatFishAndAddBear: async () => {
    const response = await fetch("/api/add-bear");
    const newBearCount = await response.json();
    const currentFish = get().fish;
    if (currentFish > 0) {
      set({ bears: newBearCount, fish: currentFish - 1 });
    }
  },
}));

// 在组件中使用
function BearCounter() {
  const bears = useBearStore((state) => state.bears); // 选择器订阅
  const increasePopulation = useBearStore((state) => state.increasePopulation);

  return (
    <div>
      <h1>{bears} around here ...</h1>
      <button onClick={increasePopulation}>Add Bear</button>
    </div>
  );
}

Jotai

Jotai 的核心概念是原子(Atom),通过组合原子来构建状态。

js 复制代码
import { atom, useAtom } from "jotai";

// 1. 创建原子
const countAtom = atom(0);
// 派生原子,用于计算衍生状态
const doubleCountAtom = atom((get) => get(countAtom) * 2);

// 2. 在组件中使用
function Counter() {
  const [count, setCount] = useAtom(countAtom);
  const [doubleCount] = useAtom(doubleCountAtom);
  return (
    <div>
      <p>
        {count} * 2 = {doubleCount}
      </p>
      <button onClick={() => setCount((c) => c + 1)}>one up</button>
    </div>
  );
}

Redux Toolkit (RTK)

RTK 是 Redux 官方推荐的、简化 Redux 使用的工具集,大幅减少了模板代码。

js 复制代码
// 1. 使用RTK创建Slice
import { createSlice, configureStore } from "@reduxjs/toolkit";

const counterSlice = createSlice({
  name: "counter",
  initialState: { value: 0 },
  reducers: {
    incremented: (state) => {
      state.value += 1;
    }, // 内部使用Immer,可直接"突变"
    decremented: (state) => {
      state.value -= 1;
    },
  },
});

// 2. 创建Store
const store = configureStore({
  reducer: counterSlice.reducer,
});

// 3. 在React组件中使用
import { useSelector, useDispatch } from "react-redux";

function Counter() {
  const count = useSelector((state) => state.value);
  const dispatch = useDispatch();
  return (
    <div>
      <span>{count}</span>
      <button onClick={() => dispatch(counterSlice.actions.incremented())}>
        Increment
      </button>
    </div>
  );
}

useReducer

useReducer 是 React 提供的一个内置 Hook,它是 useState 的替代方案,更适合管理复杂的状态逻辑。它借鉴了 Redux 的核心思想,但更加轻量化和内置化。

基本语法

js 复制代码
const [state, dispatch] = useReducer(reducer, initialState);

基本用法

js 复制代码
import { useReducer } from "react";

// 1. 定义 reducer 函数
function counterReducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    case "reset":
      return { count: 0 };
    case "set":
      return { count: action.payload };
    default:
      throw new Error(`Unknown action type: ${action.type}`);
  }
}

// 2. 在组件中使用
function Counter() {
  const [state, dispatch] = useReducer(counterReducer, { count: 0 });

  return (
    <div>
      <h1>Count: {state.count}</h1>
      <button onClick={() => dispatch({ type: "increment" })}>+1</button>
      <button onClick={() => dispatch({ type: "decrement" })}>-1</button>
      <button onClick={() => dispatch({ type: "reset" })}>Reset</button>
      <button onClick={() => dispatch({ type: "set", payload: 10 })}>
        Set to 10
      </button>
    </div>
  );
}

相对复杂用法

js 复制代码
import { useReducer, useState } from "react";

// 初始状态
const initialState = {
  todos: [],
  filter: "all", // all, active, completed
};

// Reducer 函数
function todoReducer(state, action) {
  switch (action.type) {
    case "ADD_TODO":
      return {
        ...state,
        todos: [
          ...state.todos,
          {
            id: Date.now(),
            text: action.payload,
            completed: false,
          },
        ],
      };

    case "TOGGLE_TODO":
      return {
        ...state,
        todos: state.todos.map((todo) =>
          todo.id === action.payload
            ? { ...todo, completed: !todo.completed }
            : todo
        ),
      };

    case "DELETE_TODO":
      return {
        ...state,
        todos: state.todos.filter((todo) => todo.id !== action.payload),
      };

    case "SET_FILTER":
      return {
        ...state,
        filter: action.payload,
      };

    case "CLEAR_COMPLETED":
      return {
        ...state,
        todos: state.todos.filter((todo) => !todo.completed),
      };

    default:
      return state;
  }
}

function TodoApp() {
  const [state, dispatch] = useReducer(todoReducer, initialState);
  const [inputValue, setInputValue] = useState("");

  const addTodo = () => {
    if (inputValue.trim()) {
      dispatch({ type: "ADD_TODO", payload: inputValue });
      setInputValue("");
    }
  };

  // 根据筛选条件过滤 todos
  const filteredTodos = state.todos.filter((todo) => {
    if (state.filter === "active") return !todo.completed;
    if (state.filter === "completed") return todo.completed;
    return true;
  });

  return (
    <div>
      <h1>Todo App (useReducer)</h1>

      <div>
        <input
          value={inputValue}
          onChange={(e) => setInputValue(e.target.value)}
          onKeyPress={(e) => e.key === "Enter" && addTodo()}
          placeholder="Add a new todo..."
        />
        <button onClick={addTodo}>Add</button>
      </div>

      <div>
        <button
          onClick={() => dispatch({ type: "SET_FILTER", payload: "all" })}
        >
          All
        </button>
        <button
          onClick={() => dispatch({ type: "SET_FILTER", payload: "active" })}
        >
          Active
        </button>
        <button
          onClick={() => dispatch({ type: "SET_FILTER", payload: "completed" })}
        >
          Completed
        </button>
        <button onClick={() => dispatch({ type: "CLEAR_COMPLETED" })}>
          Clear Completed
        </button>
      </div>

      <ul>
        {filteredTodos.map((todo) => (
          <li
            key={todo.id}
            style={{
              textDecoration: todo.completed ? "line-through" : "none",
            }}
          >
            <span
              onClick={() =>
                dispatch({ type: "TOGGLE_TODO", payload: todo.id })
              }
            >
              {todo.text}
            </span>
            <button
              onClick={() =>
                dispatch({ type: "DELETE_TODO", payload: todo.id })
              }
            >
              Delete
            </button>
          </li>
        ))}
      </ul>

      <div>
        <p>Total: {state.todos.length}</p>
        <p>Active: {state.todos.filter((t) => !t.completed).length}</p>
        <p>Completed: {state.todos.filter((t) => t.completed).length}</p>
      </div>
    </div>
  );
}

useReducer 最佳实践

  1. Action 类型常量
js 复制代码
// actions.js
export const ActionTypes = {
  ADD_TODO: "ADD_TODO",
  TOGGLE_TODO: "TOGGLE_TODO",
  DELETE_TODO: "DELETE_TODO",
  SET_FILTER: "SET_FILTER",
  CLEAR_COMPLETED: "CLEAR_COMPLETED",
};
  1. Action 创建函数
js 复制代码
// actionCreators.js
export const addTodo = (text) => ({
  type: ActionTypes.ADD_TODO,
  payload: text,
});

export const toggleTodo = (id) => ({
  type: ActionTypes.TOGGLE_TODO,
  payload: id,
});

// 在组件中使用
dispatch(addTodo("Learn useReducer"));
  1. 结合 Context API 实现全局状态管理
js 复制代码
import { createContext, useContext, useReducer } from "react";

const TodoContext = createContext();

export function TodoProvider({ children }) {
  const [state, dispatch] = useReducer(todoReducer, initialState);

  return (
    <TodoContext.Provider value={{ state, dispatch }}>
      {children}
    </TodoContext.Provider>
  );
}

export const useTodo = () => {
  const context = useContext(TodoContext);
  if (!context) {
    throw new Error("useTodo must be used within a TodoProvider");
  }
  return context;
};

// 在任何子组件中使用
function TodoList() {
  const { state, dispatch } = useTodo();
  // ...
}
相关推荐
PyAIGCMaster7 小时前
ERR_PNPM_ENOENT ENOENT: no such file or directory, scandir的解决方案
react.js
Jiaberrr7 小时前
解决uni-app通用上传与后端接口不匹配问题:原生上传文件方法封装 ✨
前端·javascript·uni-app
listhi5207 小时前
CSS:现代Web设计的不同技术
前端·css
南囝coding7 小时前
现代Unix命令行工具革命:30个必备替代品完整指南
前端·后端
起风了___8 小时前
Flutter 多端音频控制台:基于 audio_service 实现 iOS、Android 锁屏与通知中心播放控制
前端·flutter
作业逆流成河8 小时前
🎉 enum-plus 发布新版本了!
前端·javascript·前端框架
WYiQIU8 小时前
高级Web前端开发工程师2025年面试题总结及参考答案【含刷题资源库】
前端·vue.js·面试·职场和发展·前端框架·reactjs·飞书
WuWuII8 小时前
SSE服务端单向推送消息到前端
前端·推送
.又是新的一天.8 小时前
04-Fiddler详解+抓包定位问题
前端·测试工具·fiddler