如何使用 React Context 管理用户状态

介绍

在 React 中,数据通过 props(父组件到子组件)从上到下流动是很常见的,但这并不总是理想的。在某些情况下,您的数据需要可用于不同深度的许多嵌套组件,并且传递道具变得难以维护且容易出错。

在这种情况下,使用Redux来管理状态似乎是个好主意,但许多人认为它不应该是您的首选

React Context是跨组件共享数据的替代解决方案,而无需在每个级别手动传递 props。

在本文中,您将探索 Context API 并了解如何使用它来管理用户状态。

先决条件

要阅读本文,您需要:

  • 熟悉 React 基础知识,如嵌套组件、道具状态将是有益的。

本教程已通过 Node v15.3.0、npmv6.14.9 和reactv17.0.1 验证。

了解问题

下面是一个Page传递 auseravatarSizeprop组件示例

<Page user={user} avatarSize={avatarSize} />

它呈现一个PageLayout组件:

<PageLayout user={user} avatarSize={avatarSize} />

它呈现一个NavigationBar组件:

<NavigationBar user={user} avatarSize={avatarSize} />

渲染 aLink并且Avatar使用userandavatarSize道具:

<Link href={user.permalink}>
  <Avatar user={user} size={avatarSize} />
</Link>

在上面的例子中,只有Avatar组件实际使用了userprop。然而,它的每个祖先组件(父、祖父等)都会接收user并传递它。这意味着如果Avatar组件将来需要另一个 prop,你必须确保它的每个祖先组件接收并传递它。

实际上,user状态将需要在许多不同的组件之间共享,因此将它作为 prop 传递将导致它比上面的示例嵌套更深。

创造 React.createContext

React.createContext方法返回一个Context对象。这个Context对象带有两个重要的 React 组件,允许订阅数据:ProviderConsumer.

当 React 渲染一个订阅这个Context对象的组件时,它会从Provider树中它上面最接近的匹配组件读取当前上下文值

createContext方法接受一个可选defaultValue参数,Context.Consumer如果Context.Provider在树中找不到匹配的组件,则会提供该参数

创建一个Context对象并将其导出以供其他组件使用:

源代码/用户上下文.js
import React from 'react';

const userContext = React.createContext({user: {}});

export { userContext };

在上面的示例中,您初始化userContext并提供defaultValue{user: {}}.

现在您有了一个Context对象,您可以为它提供一个值并订阅更改。

申请 Context.Provider

将用户状态作为值传递给,context.Provider以便它可以被以下使用context.Consumer

源代码/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;

这是一个很好的开始,包装Main组件userContext.Provider确保您传递的值可以在任何子Main组件中使用。

申请 Context.Consumer

userContext.Consumer接收一个函数作为一个孩子。此函数接收当前上下文值(作为 prop 传递给 的值userContext.Provider)并返回一个 React 节点。

源代码/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。接下来,您将需要能够从嵌套的子组件更新上下文。

考虑一个用于注销用户的按钮。

在这种情况下,您可以通过相同的上下文向下传递函数,以允许使用者更新上下文。

源代码/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

源代码/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.Producerand Context.Consumer

如果您想了解有关 React 的更多信息,请查看我们的How To Code in React.js系列,或查看我们的 React 主题页面以获取练习和编程项目。

觉得文章有用?

点个广告表达一下你的爱意吧 !😁