【问题标题】:Page doesn't re-render when react context variable changes反应上下文变量更改时页面不会重新渲染
【发布时间】:2020-04-22 04:49:21
【问题描述】:

我正在使用 React 上下文来保持全局主题状态。

import React, { useState, createContext } from "react"

const ThemeContext = createContext(localStorage.getItem("theme") || "light")

export const ThemeStore = ({ children }) => {
  const [theme, setTheme] = useState(localStorage.getItem("theme") || "light")

  const handleThemeChange = theme => {
    setTheme(theme)
    localStorage.setItem("theme", theme === "dark" ? "dark" : "light")
  }

  return (
    <ThemeContext.Provider value={{ theme, changeTheme: handleThemeChange }}>
      {children}
    </ThemeContext.Provider>
  )
}

export default ThemeContext

我在布局组件中提供此上下文,以便我的整个应用程序可以访问商店。下面是我的布局组件。

import React, { useContext } from "react"
import PropTypes from "prop-types"

import Header from "./header"
import "../styles/layout.css"
import ThemeContext, { ThemeStore } from "../contexts/ThemeContext"
import MobileBottomNav from "../components/mobileBottomNav"

// Wrapper for background color
const Wrapper = ({ children }) => {
  const theme = useContext(ThemeContext)

  return (
    <div className={`${theme.theme === "dark" ? "theme-dark" : "theme-light"}`}>
      {children}
    </div>
  )
}

const Layout = ({ children, hideHeader, hideFooterNavBar }) => {
  return (
    <ThemeStore>
      <Wrapper>
        <div className="bg-bg-primary">
          <Header hideHeader={hideHeader} />
          <main>{children}</main>
          <MobileBottomNav hideFooterNavBar={hideFooterNavBar} />
        </div>
      </Wrapper>
    </ThemeStore>
  )
}

Layout.propTypes = {
  children: PropTypes.node.isRequired,
}

export default Layout

我的 Header 组件中有一个按钮,可以将主题从浅色更改为深色,反之亦然。我的菜单组件等都改变了颜色,因为我添加了条件类名。

但在我的登录页面中,也包含在提供程序周围,但在状态更改时不会更新。因此,如果我将主题从深色更改为浅色,但它不会重新呈现注册页面,因此颜色不会改变。为什么会这样?如何在主题更改时重新渲染页面以便应用新颜色。

这是我的登录页面。

import React, { useContext } from "react"

import SEO from "../components/seo"
import Layout from "../components/layout"
import ThemeContext from "../contexts/ThemeContext"

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

  // This just logs the initial value of theme, but when I change 
  // the theme from the header component, this value doesn't log again. 
  // The component doesn't re-render.
  console.log(theme)

  return (
    <Layout hideFooterNavBar={true}>
      <SEO title="Sign In" />
      <div>
        <div
          style={{
            paddingTop: "150px",
          }}
          className="container mx-auto"
        >
          <p
            className={`${
              theme.theme === "dark" ? "theme-dark" : "theme-light"
            } text-text-primary mb-10`}
          >
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. In non
            sapien blandit, aliquet lorem quis, consectetur tellus. Maecenas ac
            nibh eu enim auctor volutpat. Mauris a lacus magna. Proin
            sollicitudin mauris sit amet auctor feugiat. Nulla facilisi.
            Pellentesque eget massa nec massa porta tristique in vitae ante.
            Donec rutrum imperdiet urna.
          </p>
        </div>
      </div>
    </Layout>
  )
}

export default SignInPage

【问题讨论】:

  • 我没有从您发布的代码示例中看到任何异常。你确定你只有 1 个ThemeContext.Provider,而那个SignupPage 是那个ThemeContext.Provider 的一个孩子吗?我建议安装 React Devtools chrome 扩展,这样您就可以检查您的组件,并查看上下文提供程序的价值。
  • 不仅是注册页面,还有索引页面。两者都由 组件包裹,而 Layout 组件由具有提供程序的 组件包裹。但它不会重新渲染。
  • 这里是开发工具的截图。 imgur.com/a/59PSDdJ

标签: javascript reactjs gatsby react-context


【解决方案1】:

您的问题是您在向ThemeContext.Provider 提供值的位置下方使用ThemeContext

如果您检查 React 开发工具,您应该会看到您的树看起来像这样:

  • 登录页面
    • 布局
      • 主题商店
      • ThemeContext.Provider
        • 包装器

您可以做的是使用 Layout 的子组件中的上下文,或者使用更高阶的组件将 ThemeContext 注入 SignInPage

const SignInPage = ({ theme }) => {
  // ...
}

export default props => (
  <ThemeContext.Consumer>
    {theme => 
      <SignInPage theme={theme} {...props} />
    }
  </ThemeContext.Consumer>
)

【讨论】:

  • ``` 导出默认道具 => ( {theme => } ) ` ``没用。如果 wrapper 及其所有子项都是 ThemeContext.Provider 的子项,那么当提供程序值更改时,它下的所有组件不应该自动重新渲染吗?
  • @RahulSharma SignInPage 不是 Wrapper 的孩子;我在顶部指出了这一点。如果包装器不起作用,发生了什么?哪些数据作为theme 属性传递给SignInPage
  • @RahulSharma 你还需要这方面的帮助吗?这回答了你的问题吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-01-04
  • 1970-01-01
  • 1970-01-01
  • 2020-08-26
  • 2020-05-01
  • 1970-01-01
  • 2020-12-17
相关资源
最近更新 更多