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

ryevplcw  于 2022-11-20  发布在  React
关注(0)|答案(2)|浏览(124)

我正在使用React作为前端创建一个静态Web应用程序,但遇到了一个动态主题化问题。虽然主题切换和将主题名称保存到本地存储中的操作正常,但在页面刷新或路径改变之前,它实际上只工作一次。
例如,我可以用之前选择的深色主题加载页面,但如果我切换回浅色主题,一切都正常,但另一次切换到深色主题会导致用户界面按预期改变颜色,但本地存储变量仍将保持浅色主题。
代码如下
这些是展示和改变主题的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;

实现,它只是一个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",

刷新页面或简单地导航到主页路线,然后返回主题路线将重置循环。
我在控制台上记录了theme和themeName,希望在themeToggle中使用if语句进行调试

if (theme === themeName) {
    console.log("Theme is already set to " + themeName);
    return
} else {
    setTheme(themeName); 
    document.getElementById("themeProvider").className = themeName; 
}

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

ht4b089n

ht4b089n1#

在React中,不建议直接使用getElementById来修改DOM。如果使用这种方法,很可能会让人感到困惑,因为现在DOM既受手动控制,又受React控制。
ThemeToggle(props.themeID)有更多可疑的地方。大小写与var themeToggle不匹配,所以它可能没有运行。在这个例子中,它是不必要的,所以应该用setValue替换它。props.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;
}

如果有必要将数据传递给应用的其余部分,则可以使用上下文扩展此示例。可以使用一对上下文提供程序来 Package 整个应用,一个提供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;
23c0lvtd

23c0lvtd2#

尝试将setting方法传递给child,看起来好像child更新主题时父代没有更新。

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>
    </>
  );
}

或使用a context

相关问题