Skip to content

Reducer 和 Context

使用 Reducer 和 Context 拓展你的应用

Reducer 可以用来整合组件的状态更新逻辑。

Context 可以用来将数据深入传递给其他组件。

结合使用 Reducer 和 Context,可以共同管理一个复杂页面的状态。

基本用法

  • 首先创建一个 Context 对象用于共享值。
  • 然后创建一个 Reducer 函数用于处理状态更新逻辑。
  • 接着创建一个 Provider 组件,将 Context 对象和 Reducer 函数传递给 Provider 组件。
  • 最后在需要使用共享值的组件中使用 useContext() 消费共享值。
ts
import { createContext, Dispatch } from 'react';

interface CounterState {
    count: number;
}

interface CounterAction {
    type: string;
    payload?: any;
}

export const CounterContext = createContext<{
    state: CounterState;
    dispatch: Dispatch<CounterAction>;
}>({
    state: { count: 0 },
    dispatch: () => undefined
});
ts
export function counterReducer(state: { count: number; }, action: {
    type: string;
    payload?: any;
}) {
    switch (action.type) {
        case 'increment':
            return { count: state.count + 1 };
        case 'decrement':
            return { count: state.count - 1 };
        default:
            return state;
    }
}
tsx
import { useReducer } from "react";
import { counterReducer } from "./CounterReducer";
import { CounterContext } from "./CounterContext";

export default function CounterProvider({
    children,
}: {
    children: React.ReactNode;
}) {
    const [state, dispatch] = useReducer(counterReducer, {
        count: 0,
    });

    return (
        <CounterContext.Provider value={{ state, dispatch }}>
            {children}
        </CounterContext.Provider>
    );
}
tsx
import { useContext } from "react";
import CounterProvider from "./CounterProvider";
import { CounterContext } from "./CounterContext";

function Counter() {
    const { state, dispatch } = useContext(CounterContext);

    return (
        <div>
            <p>Count: {state.count}</p>
            <button onClick={() => dispatch({ type: "increment" })}>+</button>
            <button onClick={() => dispatch({ type: "decrement" })}>-</button>
        </div>
    );
}

export default function CounterWithReducerContext() {
    return (
        <CounterProvider>
            <Counter />
        </CounterProvider>
    );
}