状态管理

一、是什么

管理组件之间共享数据的方案。

二、方案对比

1. React 内置

React Context + useState / useReducer 是一个内置于 React 的轻量级状态管理方案。

  1. 核心思路

    创建一个 Context 对象(React.createContext)

    在顶层组件(如 App)中定义状态和更新函数

    将这些值通过 Context.Providervalue 向下传递

    任意子组件通过 useContext(Context) 获取状态和更新函数,实现“跨层级”读写

  2. 简单示例:(使用 useState)

    // 1. 创建 Context
    import React, { createContext, useState, useContext } from 'react';
    
    const ThemeContext = createContext();
    
    // 2. 创建 Provider 组件,管理状态
    function ThemeProvider({ children }) {
    const [theme, setTheme] = useState('light');
    
    const toggleTheme = () => {
        setTheme(prev => (prev === 'light' ? 'dark' : 'light'));
    };
    
    // 将状态和方法放入 value
    return (
        <ThemeContext.Provider value={{ theme, toggleTheme }}>
        {children}
        </ThemeContext.Provider>
    );
    }
    
    // 3. 自定义 Hook 方便使用
    function useTheme() {
    return useContext(ThemeContext);
    }
    
    // 4. 子组件使用
    function ThemedButton() {
        const { theme, toggleTheme } = useTheme();
        return (
            <button
            onClick={toggleTheme}
            style={{
                background: theme === 'light' ? '#fff' : '#333',
                color: theme === 'light' ? '#000' : '#fff'
            }}
            >
            当前主题:{theme},点击切换
            </button>
        );
    }
    
    function Toolbar() {
        return <ThemedButton />; // 中间可以嵌套任意深层组件
    }
    
    // 5. 顶层 App 使用 Provider
    export default function App() {
        return (
            <ThemeProvider>
            <Toolbar />
            </ThemeProvider>
        );
    }
    
  3. 复杂状态管理:待办事项(使用 useReducer + Context)

    import React, { createContext, useReducer, useContext } from 'react';
    
    // 1. 定义 state 结构和 reducer
    const initialState = {
        todos: [],
        nextId: 1
    };
    
    function todoReducer(state, action) {
        switch (action.type) {
            case 'ADD_TODO':
            return {
                ...state,
                todos: [...state.todos, { id: state.nextId, text: action.payload, completed: false }],
                nextId: state.nextId + 1
            };
            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)
            };
            default:
            return state;
        }
    }
    
    // 2. 创建 Context
    const TodoContext = createContext();
    
    // 3. Provider 组件:整合 reducer 并向下传递状态和 dispatch
    function TodoProvider({ children }) {
        const [state, dispatch] = useReducer(todoReducer, initialState);
        
        // 也可以把 dispatch 单独传递,也可以封装成 action 函数再传递,通常直接传递 dispatch 更灵活
        return (
            <TodoContext.Provider value={{ state, dispatch }}>
                {children}
            </TodoContext.Provider>
        );
    }
    
    // 4. 自定义 Hook
    function useTodos() {
        return useContext(TodoContext);
    }
    
    // 5. 展示和操作组件的示例
    function TodoList() {
        const { state, dispatch } = useTodos();
        
        return (
            <ul>
            {state.todos.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 })}>
                    删除
                </button>
                </li>
            ))}
            </ul>
        );
    }
    
    function AddTodo() {
        const { dispatch } = useTodos();
        const [text, setText] = React.useState('');
        
        const handleSubmit = (e) => {
            e.preventDefault();
            if (text.trim()) {
            dispatch({ type: 'ADD_TODO', payload: text });
            setText('');
            }
        };
        
        return (
            <form onSubmit={handleSubmit}>
            <input value={text} onChange={(e) => setText(e.target.value)} />
            <button type="submit">添加</button>
            </form>
        );
    }
    
    // 6. 顶层 App
    export default function App() {
        return (
            <TodoProvider>
                <AddTodo />
                <TodoList />
            </TodoProvider>
        );
    }
    

2. 外部库

  1. Redux(最经典)

    Redux 核心思想:单一 store、只读 state、reducer 纯函数修改。现代写法使用 @reduxjs/toolkit 和 react-redux,极大简化了样板代码。

    npm install @reduxjs/toolkit react-redux
    
    // store.js
    
    import { configureStore, createSlice } from '@reduxjs/toolkit';
    
    // 创建 slice(包含 state + reducer)
    const counterSlice = createSlice({
        name: 'counter',
        initialState: { value: 0 },
        reducers: {
            increment: (state) => { state.value += 1 },   // 允许写“可变”代码(内部用 Immer)
            decrement: (state) => { state.value -= 1 },
            addBy: (state, action) => { state.value += action.payload }
        }
    });
    
    export const { increment, decrement, addBy } = counterSlice.actions;
    
    export const store = configureStore({
        reducer: { counter: counterSlice.reducer }
    });
    
    // App.js
    import React from 'react';
    import { Provider, useSelector, useDispatch } from 'react-redux';
    import { store, increment, decrement, addBy } from './store';
    
    function Counter() {
        const count = useSelector(state => state.counter.value);
        const dispatch = useDispatch();
    
        return (
            <div>
            <h1>{count}</h1>
            <button onClick={() => dispatch(increment())}>+1</button>
            <button onClick={() => dispatch(decrement())}>-1</button>
            <button onClick={() => dispatch(addBy(5))}>+5</button>
            </div>
        );
    }
    
    export default function App() {
        return (
            <Provider store={store}>
                <Counter />
            </Provider>
        );
    }
    
  2. Zustand(极简现代)

    Zustand 不需要 Provider,直接创建 store,Hooks 式使用,代码非常少。

    npm install zustand
    
    import { create } from 'zustand';
    
    // 创建 store
    const useCounterStore = create((set) => ({
        count: 0,
        increment: () => set((state) => ({ count: state.count + 1 })),
        decrement: () => set((state) => ({ count: state.count - 1 })),
        addBy: (amount) => set((state) => ({ count: state.count + amount })),
    }));
    
    function Counter() {
        // 选择性订阅,避免不必要的重渲染
        const { count, increment, decrement, addBy } = useCounterStore();
    
        return (
            <div>
            <h1>{count}</h1>
            <button onClick={increment}>+1</button>
            <button onClick={decrement}>-1</button>
            <button onClick={() => addBy(5)}>+5</button>
            </div>
        );
    }
    
    export default function App() {
        return <Counter />; // 无需 Provider!
    }
    
  3. MobX(响应式可变状态)

    MobX 通过 @observable@action 让状态变得“可观察”,任何组件只要 observer 包裹,就会自动追踪依赖并更新。

    npm install mobx mobx-react-lite
    
    import { makeAutoObservable } from 'mobx';
    import { observer } from 'mobx-react-lite';
    import React from 'react';
    
    // 创建 store 类
    class CounterStore {
        count = 0;
    
        constructor() {
            makeAutoObservable(this); // 自动使属性可观察,方法变成 action
        }
    
        increment() {
            this.count += 1;
        }
    
        decrement() {
            this.count -= 1;
        }
    
        addBy(amount) {
            this.count += amount;
        }
    }
    
    const counterStore = new CounterStore();
    
    // 组件用 observer 包裹,自动响应变化
    const Counter = observer(() => {
        return (
            <div>
            <h1>{counterStore.count}</h1>
            <button onClick={() => counterStore.increment()}>+1</button>
            <button onClick={() => counterStore.decrement()}>-1</button>
            <button onClick={() => counterStore.addBy(5)}>+5</button>
            </div>
        );
    });
    
    export default function App() {
        return <Counter />;
    }
    

对比

代码量需要 Provider心智模型适合场景
Redux中等单向数据流 + 纯函数大型应用,严格可控
Zustand极少简单 Hooks中小型、快速开发
MobX响应式可变状态复杂对象、领域模型

三、核心原理

1. 发布订阅

store → subscribe → 更新组件

2. 单向数据流

action → reducer → state → UI

四、实践

  • 小项目:Context + Hooks
  • 大项目:Zustand / Redux Toolkit