从本地存储获取值为什么会破坏我的主题切换?

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

Why getting value from local storage breaks my theme toggle?

问题

我有这段React代码。我想将状态的一部分推送到本地存储以在页面刷新时保持选择的主题。切换本身可以正常工作,直到我想从本地存储中获取值:

但是,一旦我尝试在useEffect中像这样从本地存储获取值:

那么它就不再起作用了。我在控制台中记录了正确的值。
我还尝试将userPref添加为依赖项,但然后我会得到userPref未定义的错误。有什么建议吗?

英文:

I have this piece of code in React. I want to push a fragment of my state to local storage to keep a chosen theme on page refresh. Toggle itself works fine until I want to get the value from local storage:

import React, { useEffect, useState } from 'react';
import './ThemeToggle.css'

export function ThemeToggle() {
    const [ isDark, setIsDark ] = useState(true);

    const root = document.querySelector(':root')

    const storeUserSetPreference = (pref) => {
        localStorage.setItem("isDark", pref);
        };

    
    const getUserSetPreference = () => {
        return localStorage.getItem("isDark");
            };


    const changeThemeHandler = () => {
        setIsDark(!isDark)
        storeUserSetPreference(isDark)
    }

    

    useEffect(() => {
        let userPref = getUserSetPreference()
        console.log(userPref)

        if(!isDark){
            root.classList.add('dark')
        } else {
            root.classList.remove('dark')
        }
    }, [isDark, root.classList ])


    return (
        <div className='toggle'>
            <input type='checkbox' id='darkmode-toggle' onChange={changeThemeHandler} defaultChecked={!isDark} />
            <label for='darkmode-toggle' id='toggle-label' />
        </div>
    )
}

But as soon as I try to get value from local storage in useEffect like this:

useEffect(() => {
        let userPref = getUserSetPreference()
        console.log(userPref)

        if(!iuserPref){
            root.classList.add('dark')
        } else {
            root.classList.remove('dark')
        }
    }, [isDark, root.classList ])

then it doesn't work anymore. I get the proper value logged to my console.
I also tried adding userPref as dependency but then I get error that userPref is not defined. Any ideas?

Here is a sandbox version where I was able to replicate this issue:

Live version on codesandbox

答案1

得分: 1

Local storage 只保存字符串,而您正在将 "false" 或 "true" 保存在本地存储中,而不是实际的 false 和 true。它们是字符串,而不是布尔值,但在您的代码中,您将其用作布尔值。
如果将您的 useEffect 更改为以下内容,它将解决错误:

useEffect(() => {
    let userPref = getUserSetPreference()

    if (userPref === "false") {
        root.classList.add('dark')
    } else {
        root.classList.remove('dark')
    }
}, [isDark, root.classList])
英文:

Local storage only save strings and you are saving "false" or "true" in your local storage not false and true itself. they are string not boolean but in your code you used it as a boolean.
if you change your useEffect to this it will solve the error:

useEffect(() => {
    let userPref = getUserSetPreference()

    if(iuserPref == "false"){
        root.classList.add('dark')
    } else {
        root.classList.remove('dark')
    }
}, [isDark, root.classList])

答案2

得分: 0

LocalStorage的问题在于它只能处理字符串。当你尝试存储布尔值时,它会自动转换为字符串。因此,如果你使用localStorage.getItem('isDark'),它将返回字符串'true'或'false',但这两个字符串在JavaScript中都被视为真值。因此,你需要明确地序列化数值。

如果我理解你的代码正确,你可以显著简化它:

import React, { useEffect, useState } from "react";
import "./ThemeToggle.css";

export function ThemeToggle() {
  const [isDark, setIsDark] = useState(
    JSON.parse(localStorage.getItem("isDark"))
  );
  console.log("isDark", isDark);

  useEffect(() => {
    const rootElement = document.querySelector(":root");
    localStorage.setItem("isDark", JSON.stringify(isDark));
    if (isDark) {
      rootElement.classList.add("dark");
    } else {
      rootElement.classList.remove("dark");
    }
  }, [isDark]);

  return (
    <div className="toggle">
      <input
        type="checkbox"
        id="darkmode-toggle"
        onChange={(e) => setIsDark(e.target.checked)}
        checked={isDark}
      />
      <label htmlFor="darkmode-toggle" id="toggle-label" />
    </div>
  );
}

Sandbox

英文:

The issue with LocalStorage is that it only works with strings. When you try to store a boolean value, it gets automatically converted to a string. Therefore, if you use localStorage.getItem('isDark'), it will return either the string 'true' or 'false', but both of these strings will be considered truthy values in JavaScript. So you need to explicitly serialize values

If I understand your code correctly, you can simplify it significantly:

import React, { useEffect, useState } from &quot;react&quot;;
import &quot;./ThemeToggle.css&quot;;

export function ThemeToggle() {
  const [isDark, setIsDark] = useState(
    JSON.parse(localStorage.getItem(&quot;isDark&quot;))
  );
  console.log(&quot;isDark&quot;, isDark);

  useEffect(() =&gt; {
    const rootElement = document.querySelector(&quot;:root&quot;);
    localStorage.setItem(&quot;isDark&quot;, JSON.stringify(isDark));
    if (isDark) {
      rootElement.classList.add(&quot;dark&quot;);
    } else {
      rootElement.classList.remove(&quot;dark&quot;);
    }
  }, [isDark]);

  return (
    &lt;div className=&quot;toggle&quot;&gt;
      &lt;input
        type=&quot;checkbox&quot;
        id=&quot;darkmode-toggle&quot;
        onChange={(e) =&gt; setIsDark(e.target.checked)}
        checked={isDark}
      /&gt;
      &lt;label for=&quot;darkmode-toggle&quot; id=&quot;toggle-label&quot; /&gt;
    &lt;/div&gt;
  );
}

Sandbox

huangapple
  • 本文由 发表于 2023年2月18日 21:20:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/75493612.html
匿名

发表评论

匿名网友

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

确定