页面在 React 上下文变量更改时不重新渲染。

huangapple go评论63阅读模式
英文:

Page doesn't re-render when react context variable changes

问题

我正在使用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

我在我的Layout组件中提供了这个上下文,这样整个应用程序都可以访问存储。下面是我的Layout组件。

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"

// 背景颜色包装器
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)

  // 这只是记录主题的初始值,但当我从标题组件更改主题时,这个值不会再次记录。组件不会重新渲染。
  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

如果您有任何其他问题或需要进一步帮助,请随时提出。

英文:

I am using React context to keep a global theme state.

import React, { useState, createContext } from &quot;react&quot;
const ThemeContext = createContext(localStorage.getItem(&quot;theme&quot;) || &quot;light&quot;)
export const ThemeStore = ({ children }) =&gt; {
const [theme, setTheme] = useState(localStorage.getItem(&quot;theme&quot;) || &quot;light&quot;)
const handleThemeChange = theme =&gt; {
setTheme(theme)
localStorage.setItem(&quot;theme&quot;, theme === &quot;dark&quot; ? &quot;dark&quot; : &quot;light&quot;)
}
return (
&lt;ThemeContext.Provider value={{ theme, changeTheme: handleThemeChange }}&gt;
{children}
&lt;/ThemeContext.Provider&gt;
)
}
export default ThemeContext

I am providing this context in my Layout Component so that my entire app can access the store. Below is my Layout component.

import React, { useContext } from &quot;react&quot;
import PropTypes from &quot;prop-types&quot;
import Header from &quot;./header&quot;
import &quot;../styles/layout.css&quot;
import ThemeContext, { ThemeStore } from &quot;../contexts/ThemeContext&quot;
import MobileBottomNav from &quot;../components/mobileBottomNav&quot;
// Wrapper for background color
const Wrapper = ({ children }) =&gt; {
const theme = useContext(ThemeContext)
return (
&lt;div className={`${theme.theme === &quot;dark&quot; ? &quot;theme-dark&quot; : &quot;theme-light&quot;}`}&gt;
{children}
&lt;/div&gt;
)
}
const Layout = ({ children, hideHeader, hideFooterNavBar }) =&gt; {
return (
&lt;ThemeStore&gt;
&lt;Wrapper&gt;
&lt;div className=&quot;bg-bg-primary&quot;&gt;
&lt;Header hideHeader={hideHeader} /&gt;
&lt;main&gt;{children}&lt;/main&gt;
&lt;MobileBottomNav hideFooterNavBar={hideFooterNavBar} /&gt;
&lt;/div&gt;
&lt;/Wrapper&gt;
&lt;/ThemeStore&gt;
)
}
Layout.propTypes = {
children: PropTypes.node.isRequired,
}
export default Layout

I have a button in my Header component that changes the theme from light to dark and vice versa. And my menu component etc. all change their colors, as I've added conditional classnames.

But in my signin page, which is also wrapped around the <Layout> provider doesn't update when the state is changed. So if I change the theme from dark to light but it doesn't re-render the signup page, and hence the colors don't change. Why is it so? How do I make the page re-render when the theme changes so the new colors are applied.

Here is my signin page.

import React, { useContext } from &quot;react&quot;
import SEO from &quot;../components/seo&quot;
import Layout from &quot;../components/layout&quot;
import ThemeContext from &quot;../contexts/ThemeContext&quot;
const SignInPage = () =&gt; {
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&#39;t log again. 
// The component doesn&#39;t re-render.
console.log(theme)
return (
&lt;Layout hideFooterNavBar={true}&gt;
&lt;SEO title=&quot;Sign In&quot; /&gt;
&lt;div&gt;
&lt;div
style={{
paddingTop: &quot;150px&quot;,
}}
className=&quot;container mx-auto&quot;
&gt;
&lt;p
className={`${
theme.theme === &quot;dark&quot; ? &quot;theme-dark&quot; : &quot;theme-light&quot;
} text-text-primary mb-10`}
&gt;
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.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/Layout&gt;
)
}
export default SignInPage

答案1

得分: 1

Your issue is that you are consuming ThemeContext beneath where you're providing a value to ThemeContext.Provider.

If you check the React devtools you should see that your tree looks something like this:

  • SignInPage
    • Layout
      • ThemeStore
        • ThemeContext.Provider
          • Wrapper

What you can do instead is consume the context in a component that is a child of Layout or use a higher order component to inject the ThemeContext into SignInPage:

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

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

Your issue is that you are consuming ThemeContext beneath where you're providing a value to ThemeContext.Provider.

If you check the React devtools you should see that your tree looks something like this:

  • SignInPage
    • Layout
      • ThemeStore
        • ThemeContext.Provider
          • Wrapper

What you can do instead is consume the context in a component that is a child of Layout or use a higher order component to inject the ThemeContext into SignInPage:

const SignInPage = ({ theme }) =&gt; {
  // ...
}

export default props =&gt; (
  &lt;ThemeContext.Consumer&gt;
    {theme =&gt; 
      &lt;SignInPage theme={theme} {...props} /&gt;
    }
  &lt;/ThemeContext.Consumer&gt;
)

huangapple
  • 本文由 发表于 2020年1月4日 00:04:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/59581677.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定