发布于 2026-01-06 0 阅读
0

React:ContextAPI 作为状态解决方案?[已更新]

React:ContextAPI 作为状态解决方案?[已更新]

根据上一篇文章更新

⛔️ 先前方法存在的问题

尽管上一篇文章中的方法看似有效,但最严重的问题是任何使用 AppContext 的组件都会重新渲染,即使它使用的是 Context 中一个无关的状态对象。因此,我着手解决这个问题。

✅ 解决方案

我已更新解决方案,使用多个上下文,每个上下文对应状态的一个部分。然后,我创建了一个 AppContext,将所有上下文整合在一起,并将其封装在我的应用程序周围。


🧑‍💻 代码

您可以在Github上获取代码副本,我已经创建了一个新分支,您可以在这里找到它:

GitHub - 分离状态


创建单独的上下文

首要任务是为我所在州的每个部分创建一个新的上下文。

你会在每个代码片段中看到两个主要部分。

  1. 提供程序组件:上下文提供程序用作高阶组件,并将状态值和 setter 以对象的形式提供给该值。这允许开发人员仅解构组件中的状态或 setter。
  2. 自定义钩子:访问上下文状态。自定义钩子可以轻松访问状态,避免在任何想要使用电影状态的组件中导入 useContext 和 MoviesContext。

电影背景

import {createContext, useContext, useState} from "react";

const MoviesContext = createContext([]);

export const useMovies = () => {
    return useContext(MoviesContext);
}

export const MoviesProvider = ({children}) => {
    const [movies, setMovies] = useState([]);
    return (
        <MoviesContext.Provider value={{movies, setMovies}}>
            {children}
        </MoviesContext.Provider>
    )
}
Enter fullscreen mode Exit fullscreen mode
context/MoviesContext.js

个人资料背景

import {createContext, useContext, useState} from "react";

const ProfileContext = createContext(null);

export const useProfile = () => {
    return useContext(ProfileContext);
}

export const ProfileProvider = ({children}) => {
    const [profile, setProfile] = useState(null);
    return (
        <ProfileContext.Provider value={{profile, setProfile}}>
            {children}
        </ProfileContext.Provider>
    )
}
Enter fullscreen mode Exit fullscreen mode
context/ProfileContext.js

UiLoading 上下文

import {createContext, useContext, useState} from "react";

const UiLoadingContext = createContext(false);

export const useUiLoading = () => {
    return useContext(UiLoadingContext);
}

export const UiLoadingProvider = ({children}) => {
    const [uiLoading, setUiLoading] = useState(false);
    return (
        <UiLoadingContext.Provider value={{uiLoading, setUiLoading}}>
            {children}
        </UiLoadingContext.Provider>
    )
}

Enter fullscreen mode Exit fullscreen mode
context/UiLoadingContext.js

新的 AppContext

鉴于我现在有三个独立的上下文,与其index.js在文件中添加多个提供程序,我决定创建一个 AppContext 组件,将所有提供程序组合在一起。

据我所知,这里的顺序似乎无关紧要。如果您在评论区指出错误,请随时指正,我会更新文章。

import {ProfileProvider} from "./ProfileContext";
import {MoviesProvider} from "./MoviesContext";
import {UiLoadingProvider} from "./UiLoadingContext";

export const AppProvider = ({children}) => {
    return (
        <ProfileProvider>
            <MoviesProvider>
                <UiLoadingProvider>
                    {children}
                </UiLoadingProvider>
            </MoviesProvider>
        </ProfileProvider>
    )
}

Enter fullscreen mode Exit fullscreen mode
context/AppContext.js

利用上下文状态

由于每个上下文中都有自定义钩子,因此可以非常容易地访问状态值和/或设置器。

如果您只想更新配置文件,并且只有设置器的访问权限,则可以编写以下代码:

const Login = () => {
    console.log('Login.render()')
    const {setProfile} = useProfile();

    const onLoginClick = () => {
        setProfile({username: 'birdperson'});
    }
... // Login.js
Enter fullscreen mode Exit fullscreen mode
登录/Login.js

这里最大的“亮点”在于,现在只有使用 profile 上下文的组件才会重新渲染。这与前一篇文章的方法截然不同,前一篇文章中所有使用 AppContext 的组件都会重新渲染,即使它们没有访问 profile 状态。

如果您需要同时访问状态和设置器,可以再次使用自定义钩子,如下所示:

...
const {movies, setMovies} = useMovies();
Enter fullscreen mode Exit fullscreen mode

此外,只有使用 MoviesContext 的组件才会在调用 setMovies 设置器时重新渲染,其他组件则不会受到影响。


结论

在小型应用程序中,使用 Context 是共享状态的绝佳方式,但如果您不完全了解 ContextAPI 的工作原理,则可能会遇到一些“陷阱”。这是一次非常宝贵的学习经历,再次感谢您指出需要改进的地方。


🤓 感谢阅读 🙏
文章来源:https://dev.to/dewaldels/react-contextapi-as-a-state-solution-updated-5a2j