如何使用React上下文管理用户状态
介绍
在 React 中,数据通过 props(父组件到子组件)从上到下流动是很常见的,但这并不总是理想的。 在某些情况下,您的数据需要可用于不同深度的许多嵌套组件,并且传递道具变得难以维护且容易出错。
在这种情况下,使用 Redux 来管理状态似乎是个好主意,但许多人认为 它不应该是您的首选 。
React Context 是跨组件共享数据的替代解决方案,无需在每个级别手动传递道具。
在本文中,您将探索 Context API 并了解如何使用它来管理用户状态。
先决条件
要阅读本文,您将需要:
本教程已使用 Node v15.3.0、npm
v6.14.9 和 react
v17.0.1 进行了验证。
理解问题
这是传递 user
和 avatarSize
属性的 Page
组件的示例:
<Page user={user} avatarSize={avatarSize} />
渲染一个 PageLayout
组件:
<PageLayout user={user} avatarSize={avatarSize} />
渲染一个 NavigationBar
组件:
<NavigationBar user={user} avatarSize={avatarSize} />
渲染使用 user
和 avatarSize
道具的 Link
和 Avatar
:
<Link href={user.permalink}> <Avatar user={user} size={avatarSize} /> </Link>
在上面的例子中,只有 Avatar
组件实际上使用了 user
属性。 然而,它的每个祖先组件(父、祖父等)都会接收 user
并将其传递下去。 这意味着如果 Avatar
组件将来需要另一个 prop,您必须确保它的每个祖先组件都接收并向下传递它。
实际上,user
状态需要在许多不同的组件之间共享,因此将其作为道具传递将导致它的嵌套比上面的示例更深。
创建 React.createContext
React.createContext
方法返回一个 Context
对象。 这个 Context
对象带有两个重要的 React 组件,它们允许订阅数据:Provider
和 Consumer
。
当 React 渲染订阅此 Context
对象的组件时,它将从树中最匹配的 Provider
组件读取当前上下文值。
createContext
方法接受一个可选的 defaultValue
参数,如果在树中找不到匹配的 Context.Provider
组件,则将其提供给 Context.Consumer
。
创建一个 Context
对象并将其导出以供其他组件使用:
src/userContext.js
import React from 'react'; const userContext = React.createContext({user: {}}); export { userContext };
在上面的示例中,您初始化了 userContext
并提供了 {user: {}}
的 defaultValue
。
现在您有了一个 Context
对象,您可以为它提供一个值并订阅更改。
应用 Context.Provider
将用户状态作为值传递给 context.Provider
,以便 context.Consumer
使用它:
src/App.js
import React from 'react'; import Main from './Main'; import {userContext} from './userContext'; class App extends React.Component { constructor(props) { super(props); this.state = { user: {} }; } componentDidMount() { // get and set currently logged in user to state } render() { return ( <userContext.Provider value={this.state.user}> <Main/> </userContext.Provider> ); } } export default App;
这是很好的开始,用 userContext.Provider
包装 Main
组件可确保您传递的值可以在 Main
的任何后代子组件中使用。
应用 Context.Consumer
userContext.Consumer
作为一个子函数接受一个函数。 此函数接收当前上下文值(作为道具传递给 userContext.Provider
的值)并返回一个 React 节点。
src/Main.js
import Sidebar from './Sidebar'; import Content from './Content'; import Avatar from './Avatar'; import {userContext} from './userContext'; function Main(props) { return ( <Sidebar/> <userContext.Consumer> {({value}) => { <Avatar value={value}/> }} </userContext.Consumer> <Content/> ) } export default Main;
在这种情况下,它接收 App
组件的 state.user
作为值并渲染 Avatar
组件。
从嵌套组件更新上下文
到目前为止,您已经使用 React Context 将数据传递给需要它的组件,而无需手动传递 props。 接下来,您将需要能够从嵌套的子组件更新上下文。
考虑一个用于注销用户的按钮。
在这种情况下,您可以通过相同的上下文向下传递一个函数,以允许消费者更新上下文。
src/App.js
import React from 'react'; import Main from './Main'; import {userContext} from './userContext'; class App extends React.Component { constructor(props) { super(props); this.state = { user: {} }; this.logout = this.logout.bind(this); } logout() { this.setState({user: {}}); } componentDidMount() { // get and set currently logged in user to state } render() { const value = { user: this.state.user, logoutUser: this.logout } return ( <userContext.Provider value={value}> <Main/> </userContext.Provider> ); } } export default App;
传递给更新上下文的函数可以在 userContext.Provider
组件内的任何嵌套组件中使用。
src/Main.js
import Sidebar from './Sidebar'; import Content from './Content'; import Avatar from './Avatar'; import LogoutButton from './LogoutButton'; import {userContext} from './userContext'; function Main(props) { return ( <Sidebar/> <userContext.Consumer> {({user, logoutUser}) => { return ( <Avatar user={user}/> <LogoutButton onClick={logoutUser}/> ); }} </userContext.Consumer> <Content/> ) } export default Main;
注销按钮已添加到 Main
组件中。
这就是如何使用 React 的 Context API 来管理用户状态的示例。
结论
在本文中,您了解了 React Context 并使用了 Context.Producer
和 Context.Consumer
。
如果您想了解有关 React 的更多信息,请查看我们的 如何在 React.js 中编码,或查看 我们的 React 主题页面 以了解练习和编程项目。