【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;
相关推荐
系统集成-鹰翔8 小时前
ERP图片改名后仍命中识别缓存:缓存键、十分钟到期与调用计数
javascript·经验分享·后端
FlyWIHTSKY9 小时前
vue2和vue3有什么区别,用法上有哪些区别
前端·javascript·vue.js
程序员大阳11 小时前
副队长JavaScript教程(66)-本地存储实战:待办事项网页的开发
javascript·json·实战·待办事项·本地存储·案例
Dream-Y.ocean12 小时前
鸿蒙PC平台逻控 PLC 编辑器适配实战:基于 Electron 壳方案的纯 JavaScript 梯形图编程与仿真引擎开发
javascript·编辑器·harmonyos
前端小D14 小时前
JS核心底层
前端·javascript
Dovis(誓平步青云)14 小时前
启元 T1 Pro 人机相声搭档:语音-触摸双通道的启元机器人节奏引擎设计
开发语言·前端·javascript·microsoft·交互
codeejun15 小时前
每日一Go·MySQL-11、覆盖索引 & 回表原理
javascript·数据库·mysql
用户2986985301416 小时前
React 前端将 Excel 工作表导出为图片的技术实践
javascript·react.js·excel
麦麦麦当劳大王1 天前
【油猴脚本】Boss直聘 岗位职责复制
开发语言·前端·javascript
只睡四小时1 天前
Three.js Sub-tick:漏判 60.7%→0
开发语言·javascript·性能优化·ecmascript·游戏开发·three.js·碰撞检测