0
点赞
收藏
分享

微信扫一扫

react 响应式变量定义

米小格儿 2024-12-02 阅读 10

概述

在这里插入图片描述

Context 跨组件共享状态

在 Next 项目,封装 useContext。

AppContext.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

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

"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

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

"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

"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;

举报

相关推荐

0 条评论