专栏名称: 奇舞精选
《奇舞精选》是由奇舞团维护的前端技术公众号。除周五外,每天向大家推荐一篇前端相关技术文章,每周五向大家推送汇总周刊内容。
目录
相关文章推荐
金昌市场监管  ·  多款药品说明书修订 ·  20 小时前  
药渡  ·  司美格鲁肽,日赚5.8亿 ·  昨天  
医药经济报  ·  生物类似药市场浪花翻涌 ·  4 天前  
南京广播电视台  ·  官宣:暂停向中国市场供应 ·  3 天前  
南京广播电视台  ·  官宣:暂停向中国市场供应 ·  3 天前  
51好读  ›  专栏  ›  奇舞精选

使用 React Context API 的最佳实践

奇舞精选  · 公众号  ·  · 2024-08-07 18:00

正文

本文作者为 360 奇舞团前端开发工程师

本文将详细介绍如何使用 React 的 Context API 优雅地实现多主题切换,解决 props 穿透问题,并避免不必要的重新渲染。通过具体的示例代码,展示如何在浅色和深色模式之间切换,并探讨在实际项目中管理多个 Context 的最佳实践。

目录

  1. 什么是 React Context API,何时使用?

  2. 在浅色和深色模式之间切换 UI 主题

  • 使用属性传递方案

  • Context API 解决方案

  • 如何创建多个 React 上下文(以及为什么应该这样做)

  • 如何防止 React Context 重新渲染问题

    • 使用多个 React Context

    • 拆分组件并传递所需的值

    • 使用 React.useMemo() 组件

    什么是 React Context API,何时使用?

    React Context API 是 React 库的一部分,它允许在组件之间共享全局数据,而无需通过每层组件传递 props 。Context API 非常适合需要在多个嵌套组件中共享状态的场景,例如管理全局主题设置、用户身份验证状态或应用配置等。使用 Context API,可以避免繁琐的 props 传递,提高代码的可读性和维护性。

    以下内容将通过具体示例展示如何在 React 应用中使用 Context API 实现多主题切换,并探讨其最佳实践。

    浅色和深色模式 UI 主题

    React Context 的一个常见应用是管理浅色和深色模式的 UI 主题。许多 UI 组件,如按钮、标题、导航栏等,都需要根据当前主题显示不同的样式。通过使用 Context,可以在整个应用中方便地共享和切换主题,而不需要在每个组件中手动传递 props 。下面我们对比一下两种解决方案:使用Props传递的方案和Context API 解决方案

    使用 Props 传递的方案

    最直接的方法是通过顶层组件创建一个主题变量,然后将其作为 props 传递给组件树中的所有子组件。然而,这种方法会导致“props 穿透”问题,即每个中间组件都需要传递这个 props,即使它们并不实际使用该值。这不仅使代码变得冗长和难以维护,还可能导致中间组件在不必要的时候重新渲染,从而影响性能。

    function App({
      const theme = 'dark';
      return <Parent theme={theme} />;
    }

    function Parent({ theme }{
      return <Child theme={theme} />;
    }

    function Child({ theme }{
      return <Switch theme={theme} />;
    }

    function Switch({ theme }{
      return <Switch style={{ background: theme === 'dark' ? '#000' : '#fff' }}>切换主题Switch>;
    }

    在上述代码中, theme 属性被一层层传递到最底层的 Switch 组件。虽然这种方式能实现功能,但显然并不优雅。每个中间组件都需要接受和传递 theme 属性,即使它们并不使用这个值。这不仅增加了代码的复杂度,还导致了潜在的性能问题。

    Context API 解决方案

    我们可以通过使用 Context API 来解决 props 穿透 问题。

    创建 Context

    首先,我们需要引入 createContext ,配置所需的主题颜色,并使用 light 主题作为默认值:

    // src/contexts/ThemeContext.js
    import { createContext } from "react";
    export const themes = {
      light: {
        background"#fff",
        text"#000",
        current'light'
      },
      dark: {
        background"#000",
        text"#fff",
      },
    };
    export const ThemeContext = createContext(themes.light);
    创建 Provider 组件

    接下来,我们需要创建一个组件,通过 Context 的 Provider 来提供全局状态。这个组件通常会包含状态和操作方法,并将它们作为值传递给 Provider。例如,以下的 组件将可以访问 theme 状态:

    // src/App.js
    import React, { useState } from "react"
    import { ThemeContext, themes } from "./contexts/ThemeContext"
    import Navbar from "./components/Navbar"
    import Switch from "./components/Switch"

    const App = () => {
      const [theme, setTheme] = useState(themes.light)

      const toggleTheme = () => {
        setTheme(state => (state === themes.light ? themes.dark : themes.light))
      }

      return (
        <div className="App">
          <ThemeContext.Provider value={theme}>
            <Navbar />
            <Switch changeTheme={toggleTheme} />
          ThemeContext.Provider>
        div>

      )
    }

    export default App

    在上面的代码中,通过 ThemeContext.Provider theme setTheme 方法传递给其子组件。这样,在 Switch Navbar 组件中可以使用 useContext 钩子访问 ThemeContext

    使用 Context

    Switch Navbar 组件中,我们使用 useContext 钩子来获取当前主题,并根据主题动态更改样式:

    // src/components/Switch.js

    import React, { useContext } from "react"
    import { ThemeContext } from "../contexts/themeContext"

    const Switch = ({ changeTheme }) => {
      const theme = useContext(ThemeContext)

      return (
        <Switch
          style={{ backgroundColor: theme.background, color: theme.text }}
          onClick={changeTheme}
        >

          切换主题:{ theme.current }
        Switch>

      )
    }

    export default Switch

    // src/components/Navbar.js
    import React, { useContext } from "react"
    import { ThemeContext } from "../contexts/themeContext"

    const Navbar = () => {
      const theme = useContext(ThemeContext)

      return (
        <div style={{ backgroundColor: theme.background }}>
       <ul style={{ display: "flex",gap: "20px" }}>
            <li style={{ color: theme.text }}>首页li>
            <li style={{ color: theme.text }}>广场li>
            <li style={{ color: theme.text}}>我的li>
          ul>
        div>

      )
    }

    export default Navbar

    通过这样设置,组件可以访问全局变量,每次更新上下文中的值时,组件都会重新渲染。

    浅色模式:

    image-20240804200934957

    深色模式:

    image-20240804201002336

    如何创建多个 React Contexts

    在上面的示例中,我们只创建了一个上下文, 即 ThemeContext 。但是,如果我们还有其他需要全局使用的数据,例如当前登录用户的信息 username age ,该怎么办?我们可以创建一个大的 Context 来存储需要全局使用的所有变量:


      <Switch changeTheme={toggleTheme} />
      <Navbar />
    </GlobalContext.Provider>

    但这并不是一个好的做法。因为每当 Context 的值更新时,使用该 Context 的所有组件都会重新渲染。这意味着,每当更新任何用户的变量信息时,所有只关心主题变化而不需要关心用户数据变化的组件也会被重新渲染。这可能会降低应用的性能,尤其是在具有大量复杂组件的应用中。

    为了解决这个问题,我们可以创建多个 Context 。例如,一个用于管理主题( ThemeContext ),另一个用于管理用户数据( UserContext )。如下所示:

    // src/contexts/UserContext.js
    import { createContext } from "react";
    export const UserContext = createContext({
      username"",
      age0,
    });

    // src/App.js
    import React, { useState } from "react";
    import { ThemeContext, themes } from "./contexts/ThemeContext";
    import { UserContext } from "./contexts/UserContext";
    import Navbar from "./components/Navbar";
    import Switch from "./components/Switch";

    const App = () => {
      const [theme, setTheme] = useState(themes.light);
      const [user, setUser] = useState({ username"mario"age25 });

      const toggleTheme = () => {
        setTheme(state => (state === themes.light ? themes.dark : themes.light));
      };

      return (
        <div className="App">
          <ThemeContext.Provider value={theme}>
            <UserContext.Provider value={user}>
              <Navbar />
              <Switch changeTheme={toggleTheme} />
            UserContext.Provider>
          ThemeContext.Provider>
        div>

      );
    };

    export default App;

    通过这种方式,每个 context 中只存储与其相关的数据,这有助于防止不必要的组件重新渲染,从而提高应用程序的性能。

    如何防止 React Context 重新渲染问题

    正如上面所写的,每当更新 Context 值时,所有使用该上下文的组件都将被重新渲染——即使包装在 React.memo() 中也是如此。但我们可以通过以下方法缓解这个问题:

    1. 使用多个 React Context

    这是防止不必要重新渲染的首选方法。通过创建多个 context,将相关的数据分开存储,只有使用特定 context 的组件会因更新而重新渲染。

    2. 拆分组件并传递所需的值

    通过将组件拆分,并将所需的值作为 props 从 context 中传递,并将子组件包装在 React.memo 中。 React.memo 是一个高阶组件(HOC),用于优化函数组件,通过缓存组件防止不必要的重新渲染。只有当其 props 发生变化时,组件才会重新渲染。







    请到「今天看啥」查看全文