如何使用React上下文管理用户状态

来自菜鸟教程
跳转至:导航、​搜索

介绍

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

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

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

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

先决条件

要阅读本文,您将需要:

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

本教程已使用 Node v15.3.0、npm v6.14.9 和 react v17.0.1 进行了验证。

理解问题

这是传递 useravatarSize 属性的 Page 组件的示例:

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

渲染一个 PageLayout 组件:

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

渲染一个 NavigationBar 组件:

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

渲染使用 useravatarSize 道具的 LinkAvatar

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

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

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

创建 React.createContext

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

当 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.ProducerContext.Consumer

如果您想了解有关 React 的更多信息,请查看我们的 如何在 React.js 中编码,或查看 我们的 React 主题页面 以了解练习和编程项目。