useContext 实现更好的状态管理!1 2

2025-05-24

useContext 实现更好的状态管理!

1

2

大家好,

当多个组件共享和更新数据时,管理应用中的数据会有些棘手。useState、useReducer、useMemo 等是 React 中的一些状态管理钩子,它们非常高效,并且在不同场景下有各自的工作方式。尽管这些钩子都很有效,但在某些情况下,状态管理仍然会变得困难。

如果你已经知道为什么我们需要上下文钩子,你可以直接跳转到它的实现

假设一个应用需要处理用户数据。应用加载时,会从后端获取数据并将其存储在应用组件中。之后,这些数据会在许多其他子组件之间共享。如果子组件只是使用这些数据,那还好。但是,如果其中一个组件更新了数据,就会出现问题。

我们知道,app 组件会获取数据,我们需要使用 prop drilling 来与所有子组件共享数据。在本例中,我们创建一个 propuserData并将其传递给该 app 组件的所有子组件,使其看起来像这样:

替代文本

当子组件只是消费数据而不更新数据时,这种方法是有效的。但是,如图所示,它children four正在对我们的用户数据执行更新操作。更新完成后,新版本的数据应该可供所有其他组件使用。

如果你注意到,当应用程序很复杂并且需要处理多个组件中的多个状态时,数据传输会变得相当困难。

这些场景需要在应用中引入像 Redux 这样的状态管理库。但有了 React context,我们就可以高效、原生地进行状态管理。

PS:Redux 是一个非常优秀且功能强大的状态管理系统。对于复杂的应用程序来说,它是最佳选择。但如果应用程序只有少量共享状态,我更喜欢使用 context 而不是 Redux。

什么是 Context?

React 上下文只不过是应用程序的全局状态。它是一种使特定数据可供所有组件使用的方法,无论它们如何嵌套。上下文可以帮助您将数据及其变化广播到所有组件。这就是为什么在我们上面讨论的用例中,它是一个非常有用的状态管理钩子。

您可以在React官方文档中阅读有关 React context 的更多信息

如何使用它?

现在我们了解了 context 的含义和使用原因。接下来,让我们了解如何使用它。要在任何 React 应用中创建 context,你需要遵循以下 4 个简单步骤:
1- 创建 context
2- 创建 provider
3- 将 provider 添加到应用
4- UseContext

这些术语一开始可能会让人非常困惑。理解 context 的最好方法是,把它看作一个简单的状态,一个我们使用 useState 创建的状态。context 唯一能做的事情就是在整个应用中共享这个状态及其变化。

因此,当我们说创建上下文时,实际上是在创建状态!当我们说创建提供程序时,顾名思义,实际上是在创建一个包装器组件,用于将该状态提供给所有组件。就是这么简单!

现在,让我们深入代码并创建上下文!在下面的代码中,我们将介绍步骤 1 和 2。



// UserDetailsProvider.js

import { createContext, useState } from 'react';

//create a context, with createContext api
export const userDetailsContext = createContext();

const UserDetailsProvider = (props) => {
        // this state will be shared with all components 
    const [userDetails, setUserDetails] = useState();

    return (
                // this is the provider providing state
        <userDetailsContext.Provider value={[userDetails, setUserDetails]}>
            {props.children}
        </userDetailsContext.Provider>
    );
};

export default UserDetailsProvider;


Enter fullscreen mode Exit fullscreen mode

在上面的代码中,我们使用了createContext api 来创建我们的userDetailsContext。现在,上下文已经创建好了,所以我们需要创建一个提供程序。

在 UserDetailsProvider 函数中,我们创建了一个提供程序userDetailsContext<contextname.Provider>这是创建它的常用语法。请注意value这里的一个 prop。value prop 将始终用于向下传递共享状态。在本例中,我们同时向下传递了 state 和 setState 函数。这是因为,即使任何组件更新了状态,全局状态也会更新,并且所有组件都可以使用。

现在我们的上下文和提供程序已经创建好了。让我们将提供程序添加到应用中。这是最重要的一步,因为它将使提供程序可供所有组件使用。因此,让我们将应用组件包装在这个提供程序中。我们的应用组件将如下所示:



//App Component

import { BrowserRouter, Switch, Route } from 'react-router-dom';
import { RouteWithSubRoutes } from './utils/shared';
import UserDetailsProvider from './context/UserDetailsProvider';
import routes from './Routes';

function App() {
    return (
        <BrowserRouter>
            <Switch>
                // As login do not require the userDetails state, keeping it outside.
                <Route path='/' component={Login} exact />
                // All other routes are inside provider
                <UserDetailsProvider>
                    {routes.map((route) => (
                        <RouteWithSubRoutes key={route.key} {...route} />
                    ))}
                </UserDetailsProvider>
            </Switch>
        </BrowserRouter>
    );
}

export default App;


Enter fullscreen mode Exit fullscreen mode

在这段代码中,应用组件不会获取数据。注意,这里我们只添加了 UserDetailsProvider 中真正需要此状态的组件。

现在我们到了最后一部分,在任何组件中使用此上下文。你肯定已经猜到了,这一步需要用到useContext这个钩子,因为我们会在这里使用上下文!(猜对了就别鼓掌了😛)

这与我们使用 useState 声明状态的方式相同。例如:



// Profile.js

import { useEffect, useState, useContext } from 'react';
import { getUser } from '../../utils/api';
import { userDetailsContext } from '../../context/UserDetailsProvider';

const Profile = ({ email }) => {

  // This is how we useContext!! Similar to useState
    const [userDetails, setUserDetails] = useContext(userDetailsContext);
    const [loading, setLoading] = useState(false);

    const handleGetUser = async () => {
        try {
            setLoading(true);
            let response = await getUser(email);
            setUserDetails(response.data);
        } catch (error) {
            console.log(error);
            // TODO: better error handling
        }
        setLoading(false);
    };

    useEffect(() => {
        if (!userDetails) {
            handleGetUser();
        }
    }, []);

    return <div className='bg-gray-gray1 h-full'>// do something</div>;
};

export default Profile;


Enter fullscreen mode Exit fullscreen mode

如果你注意到的话,useContext 看起来和 useState 很像。稍后我们会像 useState 一样使用它!因此,每当 setUserDetails 函数被调用时,状态更改都会在整个应用程序中生效,从而避免了过多的 props 钻取。

好了,关于 useContext hook 就介绍到这里。我也见过很多使用 context api 来切换和设置应用主题的例子。欢迎分享你使用这个 context api 的案例。

非常感谢您阅读这篇文章,请告诉我您的评论/反馈/建议。如果您喜欢我的文章,也可以在Twitter上联系我,或者请我喝杯咖啡

继续学习🙌

文章来源:https://dev.to/hey_yogini/usecontext-for-better-state-management-51hi
PREV
调用 API 时使用 useReducer 而不是 useState!
NEXT
useAxios:使用 axios 调用 API 的简单自定义钩子