无法使用 Reactjs 多次更新本地存储



我正在使用React作为前端创建一个静态web应用程序,并且遇到了动态主题的问题。虽然切换主题并将主题名称保存到本地存储可以正常工作,但它实际上只工作一次,直到页面刷新或路由更改并返回页面。

交货。我可以用我之前选择的深色主题加载页面,但是如果我要切换回浅色主题,事情就会正常工作,但是再切换到深色主题会导致ui按预期改变颜色,但本地存储变量将保持浅色主题。

下面的代码

这些是展示和更改主题的html卡片

const ThemeCards = ( props ) => {
// eslint-disable-next-line
const [theme, setTheme] = useLocalStorage("theme-type", "theme-default");
var themeToggle = (themeName) => {
setTheme(themeName); 
document.getElementById("themeProvider").className = themeName; 
}
return (
<div className={props.className}>
<div className={props.className + "-inner"}>
<div className={props.className + "-front"}>
<div>{props.themeName}</div> 
</div>
<div className={props.className + "-back"}>

<button onClick={() => themeToggle(props.themeID)}>Toggle</button>
</div>
</div>
</div>
)
}

这是useLocalStorage.js

import { useEffect, useState } from 'react';
const useLocalStorage = (storageKey, fallbackState) => {
const [value, setValue] = useState(
JSON.parse(localStorage.getItem(storageKey)) ?? fallbackState
);
useEffect(() => {
localStorage.setItem(storageKey, JSON.stringify(value));
}, [value, storageKey]);
return [value, setValue];
}
export default useLocalStorage;

themeProvider的实现,它只是一个div的id,作为className

的字符串提供主题名
function App() {
const [theme, setTheme] = useLocalStorage("theme-type",
"theme-default");
return (
<>
<div className={theme} id="themeProvider">
<HashRouter>
<SideBar />
<NavTitle />
<Routes>
...
</Routes>
</HashRouter>
</div>
</>
);
}
export default App;

和版本
"react": "^18.1.0",
"react-dom": "^18.1.0"
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",

刷新页面或简单地导航到主路由,然后返回主题路由将重置循环。

我已经控制台记录了主题和themeName,希望在themeToggle

中使用这个if语句进行调试
if (theme === themeName) {
console.log("Theme is already set to " + themeName);
return
} else {
setTheme(themeName); 
document.getElementById("themeProvider").className = themeName; 
}

虽然当错误发生时,本地存储中只存在一个变量,但切换到任何主题都会在控制台中产生控制台日志分支,就好像说本地存储键同时表示两个字符串一样,任何指导都将受到赞赏,因为我对在浏览器中持久化数据相当陌生!

在React中不建议直接使用getElementById并修改DOM。如果这样做,很可能会让人感到困惑,因为现在DOM既被手动控制,也被React控制。

ThemeToggle(props.themeID)可疑物较多。大写与var themeToggle不匹配,因此它甚至可能没有运行。在这个例子中,它是不必要的,所以它应该被替换为仅仅setValueprops.themeID也很突出。传入的是正确的字符串吗?

当你说"好像说本地存储键同时代表两个字符串"时,你试过登录两个分支吗?React允许在呈现新数据之前呈现相同的数据,所以它可能只是记录了之前的值。

我将给定的代码粘贴到一个新的create-react-app中,并使其按预期工作:

App.jsx:

import React, {useEffect, useState} from 'react';
import "./App.css"
const useLocalStorage = (storageKey, fallbackState) => {
const [value, setValue] = useState(
JSON.parse(localStorage.getItem(storageKey)) ?? fallbackState
);
useEffect(() => {
localStorage.setItem(storageKey, JSON.stringify(value));
}, [value, storageKey]);
return [value, setValue];
}
const App = () => {
const [theme, setTheme] = useLocalStorage("theme-type", "theme-default");
const otherTheme = theme === "theme-default" ? "theme-dark" : "theme-default";
return (
<div className={"" + theme}>
<div className={theme + "-inner"}>
<div className={theme + "-front"}>
<div>{theme}</div>
</div>
<div className={theme + "-back"}>
<button onClick={() => setTheme(otherTheme)}>Toggle</button>
</div>
</div>
</div>
)
}
export default App;

App.css:

.theme-default {
padding: 10px;
background: #f00;
}
.theme-default-inner {
padding: 10px;
background: #0f0;
}
.theme-default-front {
padding: 10px;
background: #ff0;
}
.theme-default-back {
padding: 10px;
background: #00f;
}
.theme-dark {
padding: 10px;
background: #a00;
}
.theme-dark-inner {
padding: 10px;
background: #0a0;
}
.theme-dark-front {
padding: 10px;
background: #aa0;
}
.theme-dark-back {
padding: 10px;
background: #00a;
}

如果有必要将数据传递给应用程序的其余部分,那么可以使用Context扩展这个例子。一对上下文提供程序可以用来包装整个应用程序,一个提供theme,一个提供setTheme。它们可以用useContext检索。更高级的用例将由以下内容满足:

import React, {createContext, useContext, useEffect, useState} from 'react';
import "./App.css"
const ThemeContext = createContext("theme-default");
const SetThemeContext = createContext((_) => {});
const useLocalStorage = (storageKey, fallbackState) => {
const [value, setValue] = useState(
JSON.parse(localStorage.getItem(storageKey)) ?? fallbackState
);
useEffect(() => {
localStorage.setItem(storageKey, JSON.stringify(value));
}, [value, storageKey]);
return [value, setValue];
}
const ThemeCards = (props) => {
const setTheme = useContext(SetThemeContext);
return (
<div className={props.className}>
<div className={props.className + "-inner"}>
<div className={props.className + "-front"}>
<div>{props.className}</div>
</div>
<div className={props.className + "-back"}>
<button onClick={() => setTheme(props.className)}>Toggle</button>
</div>
</div>
</div>
)
}
const OtherComponent = () => {
const theme = useContext(ThemeContext);
return (
<article className={"" + theme}>Current theme is {theme}</article>
)
}
const App = () => {
const [theme, setTheme] = useLocalStorage("theme-type", "theme-default");
return (
<SetThemeContext.Provider value={setTheme}>
<ThemeContext.Provider value={theme}>
<OtherComponent/>
<br/>
<ThemeCards className={"theme-default"}></ThemeCards>
<ThemeCards className={"theme-dark"}></ThemeCards>
</ThemeContext.Provider>
</SetThemeContext.Provider>
)
}
export default App;

尝试将设置方法传递给子元素,当子元素更新主题时,父元素似乎没有更新。

const [theme, setTheme] = useLocalStorage("theme-type",
"theme-default");
return (
<>
<div className={theme} id="themeProvider">
<HashRouter>
<SideBar />
<NavTitle />
<Routes>
...
<Route path={`/${appRoutes.home}`} element={<Home setTheme={setTheme} />} />
</Routes>
</HashRouter>
</div>
</>
);
}

或使用上下文

最新更新