【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;
相关推荐
刀法如飞22 分钟前
TypeScript 数组去重的 20 种实现方式,哪一种你还不知道?
前端·javascript·算法
_风满楼1 小时前
TDD实战-会议室冲突检测的红绿重构循环
前端·javascript·算法
Rkgua2 小时前
JS中的惰性函数基本介绍
前端·javascript
客场消音器2 小时前
我用两周半 Vibe Coding 做了一个前额叶训练的微信小程序
前端·javascript·后端
不考研当牛马4 小时前
HTML CSS 新手大全初学者必看 (含有部分 JavaScript)
javascript·css·html
卷帘依旧4 小时前
Promise链式调用原理
前端·javascript
光影少年4 小时前
react 单向数据流理解
前端·react.js·掘金·金石计划
threelab4 小时前
Three.js 图像粒子飞线效果 | 三维可视化 / AI 提示词
开发语言·javascript·人工智能
暗不需求5 小时前
告别“class 命名地狱”:从面向对象 CSS 到原子 CSS(Tailwind) 的思维跃迁
前端·css·react.js
Mr数据杨6 小时前
【Codex】用PPT文案额外描述优化课件生成细节
java·javascript·django·powerpoint·codex·项目开发