发布于 2026-01-06 6 阅读
0

React Context+Hooks API => 理想的状态管理

React Context+Hooks API => 理想的状态管理

嗨!React 功能强大,用途广泛。但有时我们需要全局状态,例如 UI 主题或语言环境偏好设置。通常情况下,我们会通过 props 将状态传递给子组件。但对于全局状态,我们需要将 props 多次传递到组件树的各个层级,就像土豆的根一样(如果你看过Women Of React 2020的演讲的话)。这就造成了一种被称为“prop 钻”的繁琐现象。这意味着我们需要将 props 从祖父组件传递到父组件,再传递到子组件,以此类推。

为了解决这个问题,你可以使用 Redux 之类的工具,这当然是一个不错的解决方案,但它会重构你的整个代码,并需要编写大量的样板代码。这使得它不适合轻量级实现。不过请记住,它不会影响性能。

那我们该怎么办?

进入 React Context API。

上下文提供了一种在组件树中传递数据的方法,而无需在每一层手动传递 props。

这是 React 官方文档的介绍。Context 是在 React 16.3 版本中引入的,它解决了全局状态管理的问题。Context 通常被认为是 Redux 的轻量级替代方案,能够提供更简洁的代码。那么,让我们开始吧!

那么,让我们创建一个简单的 React 应用。使用 `react-app create-react-app-build` 生成一个应用。然后在 `react-app-build` 中编写以下代码。App.js

function App() {
  return (
    <div className="App">
      <AppBar theme="white" />
    </div>
  );
}

function AppBar({theme}) {
  return(
    <div className="AppBar">
      <ThemedButton theme={theme}/>
    </div>
  );
}

function ThemedButton({theme}) {
  return(
    <div>
      <button style={{backgroundColor: theme}} />
    </div>
  )
}   
export default App;
Enter fullscreen mode Exit fullscreen mode

如上所示,我们需要将主题属性传递给所有组件,以便将其应用于子元素。这对于三个组件来说或许不错,但想象一下一个完整的动态网站,其组件树可能非常庞大且复杂。

那么我们来用 React Context 试试同样的方法。不过在使用 Context 之前,你需要记住它并不适用于组件数量少、属性也少的情况。对于这种情况,简单的属性线程和组件组合会更加简单。所以请谨慎使用 Context。

const ThemeContext = React.createContext('white');

function App() {
  return (
    <div className="App">
      <ThemeContext.Provider value={"black"}>
        <AppBar />
      </ThemeContext.Provider>
    </div>
  );
}

function AppBar() {
  return(
    <div className="AppBar">
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  return(
    <div>
      <ThemeContext.Consumer>
        {value => <button style={{backgroundColor: value}} />}
      </ThemeContext.Consumer>
    </div>
  )
}   
export default App;
Enter fullscreen mode Exit fullscreen mode

好的,这里有很多新代码,如果你仔细观察,你会发现 ` AppBarand`ThemedButton组件中的 props 都消失了。这是怎么回事呢?让我们来逐一分析一下。

所以请注意代码片段顶部的这行代码:

const ThemeContext = React.createContext('white');
Enter fullscreen mode Exit fullscreen mode

这就是创建 React Context 对象的过程。每个 Context 对象都包含一个 Provider 和一个 Consumer。如果您参考上面的代码,就会看到它们。

提供者和消费者

//Provider
      <ThemeContext.Provider value={"black"}>
      </ThemeContext.Provider>
//Consumer
      <ThemeContext.Consumer>
      </ThemeContext.Consumer>
Enter fullscreen mode Exit fullscreen mode

Provider 组件允许使用组件订阅上下文更改。

它接受一个 value 属性,该属性将传递给作为此提供程序子组件的使用者。因此,一个提供程序可以连接到多个使用者。提供程序甚至可以嵌套,以覆盖组件树中更深层的值。

当 Provider 的 value prop 发生变化时,所有作为 Provider 后代的消费者都会重新渲染。

Consumer 组件是负责订阅上下文变化的组件。但是,Consumer 组件需要一个类似render props 的函数作为子组件。该函数接收当前上下文值并返回一个 React 节点。

传递给函数的 value 参数将等于树状结构中与此上下文最接近的 Provider 的 value 属性。因此,在上面的代码中,我使用该值来为按钮着色。

      <ThemeContext.Consumer>
        {value => <button style={{backgroundColor: value}} />}
      </ThemeContext.Consumer>
Enter fullscreen mode Exit fullscreen mode

获取上下文的值。

现在您已经了解如何使用 Context API 了。但是,如果您查看 Provider 并思考其使用场景,您很快就会意识到,从 JSX 代码中提取上下文以实现其他功能有点困难。当然,有一些变通方法,但这并非理想之选。您可能在某些地方见过类似的方法,但那通常是遗留问题。

如果ThemedButton它是一个类组件,我们就可以使用 contextType 提取上下文。

类上的 `on`属性contextType property可以赋值给一个 `Context` 对象。这样,你就可以使用 `.` 来获取该 `Context` 类型的当前值this.context。你可以在任何生命周期方法(包括渲染函数)中引用它。所以我们可以这样实现它。

static contextType = ThemeContext;
  render() {
    return <Button theme={this.context} />;
  }
Enter fullscreen mode Exit fullscreen mode

但是,我们使用的是轻量级函数式组件和 Hooks!所以,让我们对现有代码进行一些重构。

import React, { useContext } from 'react';

const ThemeContext = React.createContext('white');

function App() {
  return (
    <div className="App">
      <ThemeContext.Provider value={"black"}>
        <AppBar />
      </ThemeContext.Provider>
    </div>
  );
}

function AppBar() {
  return(
    <div className="AppBar">
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext)
  return(
    <div>
        <button style={{backgroundColor: theme}} />
    </div>
  )
}   
export default App;
Enter fullscreen mode Exit fullscreen mode

这里我们使用了useContext钩子函数,它相当于函数式组件contextType。有了它useContext,我们就可以省去提供程序,直接在 JSX 代码之外获取当前上下文值。

更新我们的背景

更新上下文就像更新状态一样简单。使用函数式组件,我们可以useState通过传递一个函数来实现这一点,该函数将更新我们的上下文。

import React, { useState, useContext } from "react";

const ThemeContext = React.createContext({ theme: "white", toggler: () => {} });

function App() {
  const [color, setColor] = useState("white");
  const toPass = {
    theme: color,
    toggler: () => {
      return color === "white" ? setColor("black") : setColor("white");
    },
  };
  return (
    <div className="App">
      <ThemeContext.Provider value={toPass}>
        <AppBar />
      </ThemeContext.Provider>
    </div>
  );
}

function AppBar() {
  return (
    <div className="AppBar">
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const context = useContext(ThemeContext);
  return (
    <div>
      <button
        style={{ backgroundColor: context.theme }}
        onClick={context.toggler}
      />
    </div>
  );
}
export default App;
Enter fullscreen mode Exit fullscreen mode

如上所示,color状态是通过一个切换函数来操作的,该函数通过上下文传递下去。然后,子组件中的按钮会调用全局上下文中的切换函数,从而更新全局上下文。

好了!现在你知道如何利用 Context 和 Hooks 来维护组件树中的全局状态了。

如果您想深入了解 Context,请阅读官方文档。

上下文 - React

如有任何疑问,请联系我的社交媒体GitHub

文章来源:https://dev.to/gdsckiitdev/react-context-hooks-api-ideal-state-management-23ag