React:ContextAPI 作为状态解决方案?[已更新]
根据上一篇文章更新
文章已失效
⛔️ 先前方法存在的问题
尽管上一篇文章中的方法看似有效,但最严重的问题是任何使用 AppContext 的组件都会重新渲染,即使它使用的是 Context 中一个无关的状态对象。因此,我着手解决这个问题。
✅ 解决方案
我已更新解决方案,使用多个上下文,每个上下文对应状态的一个部分。然后,我创建了一个 AppContext,将所有上下文整合在一起,并将其封装在我的应用程序周围。
🧑💻 代码
您可以在Github上获取代码副本,我已经创建了一个新分支,您可以在这里找到它:
创建单独的上下文
首要任务是为我所在州的每个部分创建一个新的上下文。
你会在每个代码片段中看到两个主要部分。
- 提供程序组件:上下文提供程序用作高阶组件,并将状态值和 setter 以对象的形式提供给该值。这允许开发人员仅解构组件中的状态或 setter。
- 自定义钩子:访问上下文状态。自定义钩子可以轻松访问状态,避免在任何想要使用电影状态的组件中导入 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>
)
}
个人资料背景
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>
)
}
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>
)
}
新的 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>
)
}
利用上下文状态
由于每个上下文中都有自定义钩子,因此可以非常容易地访问状态值和/或设置器。
如果您只想更新配置文件,并且只有设置器的访问权限,则可以编写以下代码:
const Login = () => {
console.log('Login.render()')
const {setProfile} = useProfile();
const onLoginClick = () => {
setProfile({username: 'birdperson'});
}
... // Login.js
这里最大的“亮点”在于,现在只有使用 profile 上下文的组件才会重新渲染。这与前一篇文章的方法截然不同,前一篇文章中所有使用 AppContext 的组件都会重新渲染,即使它们没有访问 profile 状态。
如果您需要同时访问状态和设置器,可以再次使用自定义钩子,如下所示:
...
const {movies, setMovies} = useMovies();
此外,只有使用 MoviesContext 的组件才会在调用 setMovies 设置器时重新渲染,其他组件则不会受到影响。
结论
在小型应用程序中,使用 Context 是共享状态的绝佳方式,但如果您不完全了解 ContextAPI 的工作原理,则可能会遇到一些“陷阱”。这是一次非常宝贵的学习经历,再次感谢您指出需要改进的地方。