我有一个react web应用程序,在导航栏上有主题切换功能。我有一个ThemeProvider Context
,它有逻辑自动检测用户的系统主题偏好并设置它。然而,我觉得用户应该能够在网站上来回切换主题,而不管他们的系统偏好。下面是ThemeContext.js
文件,包含所有的主题逻辑,包括toggle
方法。
import React, { useState, useLayoutEffect } from 'react';
const ThemeContext = React.createContext({
dark: false,
toggle: () => {},
});
export default ThemeContext;
export function ThemeProvider({ children }) {
// keeps state of the current theme
const [dark, setDark] = useState(false);
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)')
.matches;
const prefersLight = window.matchMedia('(prefers-color-scheme: light)')
.matches;
const prefersNotSet = window.matchMedia(
'(prefers-color-scheme: no-preference)'
).matches;
// paints the app before it renders elements
useLayoutEffect(() => {
// Media Hook to check what theme user prefers
if (prefersDark) {
setDark(true);
}
if (prefersLight) {
setDark(false);
}
if (prefersNotSet) {
setDark(true);
}
applyTheme();
// if state changes, repaints the app
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dark]);
// rewrites set of css variablels/colors
const applyTheme = () => {
let theme;
if (dark) {
theme = darkTheme;
}
if (!dark) {
theme = lightTheme;
}
const root = document.getElementsByTagName('html')[0];
root.style.cssText = theme.join(';');
};
const toggle = () => {
console.log('Toggle Method Called');
// A smooth transition on theme switch
const body = document.getElementsByTagName('body')[0];
body.style.cssText = 'transition: background .5s ease';
setDark(!dark);
};
return (
<ThemeContext.Provider
value={{
dark,
toggle,
}}>
{children}
</ThemeContext.Provider>
);
}
// styles
const lightTheme = [
'--bg-color: var(--color-white)',
'--text-color-primary: var(--color-black)',
'--text-color-secondary: var(--color-prussianBlue)',
'--text-color-tertiary:var(--color-azureRadiance)',
'--fill-switch: var(--color-prussianBlue)',
'--fill-primary:var(--color-prussianBlue)',
];
const darkTheme = [
'--bg-color: var(--color-mirage)',
'--text-color-primary: var(--color-white)',
'--text-color-secondary: var(--color-iron)',
'--text-color-tertiary: var(--color-white)',
'--fill-switch: var(--color-gold)',
'--fill-primary:var(--color-white)',
];
因此,当页面加载时,显示用户的系统首选它们,但也允许用户通过单击触发toggle
函数的切换按钮来切换主题。在我当前的代码中,当调用toggle
时,似乎状态更改发生了两次,因此主题保持不变。我如何确保toggle
方法正确工作?
下面是所讨论的web app
4条答案
按热度按时间zf2sa74q1#
尽管巴里的解决方案是可行的,但请注意,您不必添加更多代码,只需略读代码即可获得相同的结果:
关键是将用户的偏好设置为初始状态,并在效果中停止勾选:
CodeSandbox example
7d7tgy0s2#
为什么不简单地使用
useEffect
呢?从
useEffect
访问window
的原因:Window is not defined in Next.js React app .sirbozc53#
问题是
dark
的值每次改变,useLayoutEffect
的整个块都会运行,所以当用户切换dark
时,prefers...
if语句运行,setDark
返回到系统首选项。要解决这个问题,您需要跟踪用户手动切换主题,然后阻止
prefers...
if语句运行。在
ThemeProvider
中执行以下操作:toggle
函数:useLayout
更新为如下所示:您的切换组件不需要更改。
Sal的答案是一个很好的选择。我的答案指出了现有代码中的缺陷以及如何添加。这指出了如何更有效地添加代码。
k4ymrczo4#
对于希望订阅系统范围配色方案更改的每个人:
我扩展了“丹尼尔·丹尼莱茨基的伟大回答:
通过在媒体查询中添加一个事件监听器,你可以监听黑暗主题的变化。如果你的用户有一个基于当前时间的自适应黑暗/明亮模式循环,这是很有用的。