我有以下问题:
我有一个gatsby
网站,在 js 中使用emotion
for css。我使用emotion theming
来实现暗模式。当我运行gatsby develop
时,暗模式按预期工作,但如果我使用gatsby build && gatsby serve
运行它,则不起作用。更具体地说,暗模式仅在切换到浅色并再次返回后才有效。
我必须遵循处理主题的顶级组件:
const Layout = ({ children }) => {
const [isDark, setIsDark] = useState(() => getInitialIsDark())
useEffect(() => {
if (typeof window !== "undefined") {
console.log("save is dark " + isDark)
window.localStorage.setItem("theming:isDark", isDark.toString())
}
}, [isDark])
return (
<ThemeProvider theme={isDark ? themeDark : themeLight}>
<ThemedLayout setIsDark={() => setIsDark(!isDark)} isDark={isDark}>{children}</ThemedLayout>
</ThemeProvider>
)
}
getInitalIsDark
函数检查本地存储值、操作系统配色方案,并默认为 false。如果我运行应用程序并激活暗模式,则会设置 localStorage 值。如果我现在重新加载应用程序,getInitialIsDark 方法返回 true,但 UI 呈现浅色主题。在明暗之间来回切换按预期工作,只是初始负载不起作用。
如果我将getInitialIsDark替换为true
加载黑暗模式按预期工作,但lightMode被破坏了。我让它工作的唯一方法是使用以下代码按时加载后自动重新渲染。
const Layout = ({ children }) => {
const [isDark, setIsDark] = useState(false)
const [isReady, setIsReady] = useState(false)
useEffect(() => {
if (typeof window !== "undefined" && isReady) {
console.log("save is dark " + isDark)
window.localStorage.setItem("theming:isDark", isDark.toString())
}
}, [isDark, isReady])
useEffect(() => setIsReady(true), [])
useEffect(() => {
const useDark = getInitialIsDark()
console.log("init is dark " + useDark)
setIsDark(useDark)
}, [])
return (
<ThemeProvider theme={isDark ? themeDark : themeLight}>
{isReady ? (<ThemedLayout setIsDark={() => setIsDark(!isDark)} isDark={isDark}>{children}</ThemedLayout>) : <div/>}
</ThemeProvider>
)
}
但这会导致页面加载时出现丑陋的闪烁。
我在第一种方法中的钩子上做错了什么,初始值没有按我的预期工作。
您是否尝试过这样设置初始状态?
const [isDark, setIsDark] = useState(getInitialIsDark())
请注意,我没有将getInitialIsDark((包装在一个附加函数中:
useState(() => getInitialIsDark())
您可能会使构建崩溃,因为localStorage
未在构建时定义。您可能需要检查getInitialIsDark
中是否存在。
希望这有帮助!
@PedroFilipe是正确的,useState(() => getInitialIsDark())
不是在启动时调用检查功能的方法。表达式() => getInitialIsDark()
是真实的,因此根据<ThemedLayout isDark={isDark}>
如何使用道具,它可能会意外工作,但 useState 不会评估传入的功能(据我所知(。
使用初始值const [myValue, setMyValue] = useState(someInitialValue)
myValue
中看到的值可能会滞后。我不确定为什么,但这似乎是钩子问题的常见原因。
如果组件总是渲染多次(例如,其他内容是异步的(,则不会出现问题,因为在第二次渲染中,变量将具有预期值。
为了确保在启动时检查本地存储,您需要一个额外的 useEffect(( 来显式调用您的函数。
useEffect(() => {
setIsDark(getInitialIsDark());
}, [getInitialIsDark]); //dependency only needed to satisfy linter, essentially runs on mount.
尽管大多数useEffect
示例都使用匿名函数,但您可能会发现使用命名函数更容易理解(遵循将函数名称用于文档的干净代码原则(
useEffect(function checkOnMount() {
setIsDark(getInitialIsDark());
}, [getInitialIsDark]);
useEffect(function persistOnChange() {
if (typeof window !== "undefined" && isReady) {
console.log("save is dark " + isDark)
window.localStorage.setItem("theming:isDark", isDark.toString())
}
}, [isDark])
我遇到了类似的问题,某些样式没有生效,因为它们是通过挂载时设置的类应用的(就像你只在生产构建上一样,在开发中一切正常(。
我最终将 React 使用的水合物函数从ReactDOM.hydrate
切换到ReactDOM.render
,问题消失了。
// gatsby-browser.js
export const replaceHydrateFunction = () => (element, container, callback) => {
ReactDOM.render(element, container, callback);
};
这对我有用,试试这个,让我知道它是否有效。
第一
在src/components/中,我创建了一个组件导航.js
export default class Navigation extends Component {
static contextType = ThemeContext // eslint-disable-line
render() {
const theme = this.context
return (
<nav className={'nav scroll' : 'nav'}>
<div className="nav-container">
<button
className="dark-switcher"
onClick={theme.toggleDark}
title="Toggle Dark Mode"
>
</button>
</div>
</nav>
)
}
}
第二
创建了一个盖茨比浏览器.js
import React from 'react'
import { ThemeProvider } from './src/context/ThemeContext'
export const wrapRootElement = ({ element }) => <ThemeProvider>{element}</ThemeProvider>
第三
我在 src/context/中创建了一个ThemeContext.js文件
import React, { Component } from 'react'
const defaultState = {
dark: false,
notFound: false,
toggleDark: () => {},
}
const ThemeContext = React.createContext(defaultState)
class ThemeProvider extends Component {
state = {
dark: false,
notFound: false,
}
componentDidMount() {
const lsDark = JSON.parse(localStorage.getItem('dark'))
if (lsDark) {
this.setState({ dark: lsDark })
}
}
componentDidUpdate(prevState) {
const { dark } = this.state
if (prevState.dark !== dark) {
localStorage.setItem('dark', JSON.stringify(dark))
}
}
toggleDark = () => {
this.setState(prevState => ({ dark: !prevState.dark }))
}
setNotFound = () => {
this.setState({ notFound: true })
}
setFound = () => {
this.setState({ notFound: false })
}
render() {
const { children } = this.props
const { dark, notFound } = this.state
return (
<ThemeContext.Provider
value={{
dark,
notFound,
setFound: this.setFound,
setNotFound: this.setNotFound,
toggleDark: this.toggleDark,
}}
>
{children}
</ThemeContext.Provider>
)
}
}
export default ThemeContext
export { ThemeProvider }
这应该对你有用,这是我从盖茨比官方网站上遵循的参考