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;
在上面的代码中,我们使用了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;
在这段代码中,应用组件不会获取数据。注意,这里我们只添加了 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;
如果你注意到的话,useContext 看起来和 useState 很像。稍后我们会像 useState 一样使用它!因此,每当 setUserDetails 函数被调用时,状态更改都会在整个应用程序中生效,从而避免了过多的 props 钻取。
好了,关于 useContext hook 就介绍到这里。我也见过很多使用 context api 来切换和设置应用主题的例子。欢迎分享你使用这个 context api 的案例。
非常感谢您阅读这篇文章,请告诉我您的评论/反馈/建议。如果您喜欢我的文章,也可以在Twitter上联系我,或者请我喝杯咖啡。
继续学习🙌
文章来源:https://dev.to/hey_yogini/usecontext-for-better-state-management-51hi