【React】全局状态管理(Context, Reducer)

以下为知行小课学习笔记。

概述

Context 跨组件共享状态

在 Next 项目,封装 useContext。

AppContext.tsx

tsx 复制代码
"use client";

import React, {createContext, Dispatch, ReactNode, SetStateAction, useContext, useMemo, useState} from 'react';

type State = {
    displayNavigation: boolean;
    themeMode: 'light' | 'dark';
};

type AppContextProps = {
    state: State
    setState: Dispatch<SetStateAction<State>>
}

const AppContext = createContext<AppContextProps>(null!)

export function useAppContext() {
    return useContext(AppContext)
}

export default function AppContextProvider({children}: { children: ReactNode }) {
    const [state, setState] = useState<State>({displayNavigation: true, themeMode: 'light'})
    // 性能优化
    const contextValue = useMemo(() => {
        return {state, setState}
    }, [state, setState])
    return <AppContext.Provider value={contextValue}>
        {children}
    </AppContext.Provider>
}

使用自定义封装的 useContext 和 ContextProvider 。

layouts.tsx

tsx 复制代码
import type {Metadata} from "next";
import "./globals.css";
import AppContextProvider from "@/components/AppContext";

export const metadata: Metadata = {
  title: "XIU-GPT",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="zh">
      <body>
       <AppContextProvider>{children}</AppContextProvider>
      </body>
    </html>
  );
}

Toolbar.tsx

tsx 复制代码
"use client"
import React from 'react';
import Button from "@/components/common/Button";
import {useAppContext} from "@/components/AppContext";
import {MdDarkMode, MdInfo, MdLightMode} from "react-icons/md";

function Toolbar() {
    const {state: {themeMode}, setState} = useAppContext()
    return (
        <div className={`absolute left-0 right-0 bottom-0 dark:bg-gray-800 dark:border-gray-800 border-t-2 flex p-2 justify-between`}>
            <Button icon={themeMode === 'dark' ? MdDarkMode : MdLightMode} variant="text"
                    onClick={() => {
                        setState((prevState) => {
                            return {
                                ...prevState,
                                themeMode: prevState.themeMode === 'dark' ? 'light' : 'dark'
                            }
                        })
                    }}/>
            <Button icon={MdInfo} variant="text"/>
        </div>
    );
}

export default Toolbar;

Reducer 复杂状态管理

setState 如果每次执行只是更新少量 state ,但都需要重新 set 所有 state,更新状态会变得繁琐,尤其是在 state 层级较多的情况下。useReducer 抽离 setState 逻辑,更好管理状态。

reducers/AppReducers.ts

ts 复制代码
import {ReducerWithoutAction} from "react";

export type State = {
    displayNavigation: boolean;
    themeMode: 'light' | 'dark';
};

export enum ActionType {
    UPDATE = "UPDATE"
}

type UpdateAction = {
    type: ActionType.UPDATE;
    field: string;
    value: any;
}

export type Action = UpdateAction;

export const initState: State = {
    displayNavigation: true,
    themeMode: 'light'
}

export function reducer(state: State, action: Action) {
    switch (action.type) {
        case ActionType.UPDATE:
            return { ...state, [action.field]: action.value}
        default: throw new Error(`Unhandled action type: ${action.type}`)
    }
}

AppContext.tsx

tsx 复制代码
"use client";

import React, {createContext, Dispatch, ReactNode, ReducerWithoutAction, useContext, useMemo, useReducer} from 'react';
import {Action, initState, reducer, State} from "@/reducers/AppReducers";


type AppContextProps = {
    state: State
    dispatch: Dispatch<Action>
}

const AppContext = createContext<AppContextProps>(null!)

export function useAppContext() {
    return useContext(AppContext)
}

export default function AppContextProvider({children}: { children: ReactNode }) {
    const [state, dispatch] = useReducer(reducer as ReducerWithoutAction<any>, initState, () => {
        return initState
    })
    const contextValue = useMemo(() => {
        return {state, dispatch}
    }, [state, dispatch])
    return <AppContext.Provider value={contextValue}>
        {children}
    </AppContext.Provider>
}

Toolbar.tsx

tsx 复制代码
"use client"
import React from 'react';
import Button from "@/components/common/Button";
import {useAppContext} from "@/components/AppContext";
import {MdDarkMode, MdInfo, MdLightMode} from "react-icons/md";
import {ActionType} from "@/reducers/AppReducers";

function Toolbar() {
    const {state: {themeMode}, dispatch} = useAppContext()
    return (
        <div className={`absolute left-0 right-0 bottom-0 dark:bg-gray-800 dark:border-gray-800 border-t-2 flex p-2 justify-between`}>
            <Button icon={themeMode === 'dark' ? MdDarkMode : MdLightMode} variant="text"
                    onClick={() => {
                        dispatch({
                            type: ActionType.UPDATE,
                            field: "themeMode",
                            value: themeMode === 'dark' ? 'light' : 'dark'
                        })
                    }}/>
            <Button icon={MdInfo} variant="text"/>
        </div>
    );
}

export default Toolbar;
相关推荐
HUMHSX40 分钟前
Vue 组件通信核心知识点梳理
javascript·vue.js·ecmascript
风止何安啊1 小时前
🚦 前端并发请求 “交通管制”:别让你的接口堵成早高峰
前端·javascript·面试
熊猫钓鱼>_>1 小时前
2026 前端的三场底层革命:当 Vue、React Native 和 Three.js 同时去掉中间层
前端·javascript·vue.js·react native·架构·webgl·three.js
名字还没想好☜1 小时前
React useEffect 依赖数组踩坑:闭包陷阱与清理函数
前端·javascript·react.js·react·useeffect
柒和远方3 小时前
LeetCode 4. 寻找两个正序数组的中位数 —— 二分划分的艺术
javascript·python·算法
默_笙3 小时前
😢 我被 JS 的 this 整不会了:同一个函数,换个“地区”调用,结果就不一样
javascript
Tom@敲代码3 小时前
js学习笔记-01
javascript·笔记·学习
木西3 小时前
告别机械重复:使用 Node.js + Playwright 构建 24 小时全自动测试网领水脚本
前端·javascript·node.js
不好听6134 小时前
JavaScript Promise 从入门到精通(一):为什么需要 Promise 以及它是什么
javascript
qdkfz4 小时前
JavaScript 如何解析 Outlook .msg 文件 —— @kenjiuno/msgreader
前端·javascript