skip to Main Content

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

2

Answers


  1. 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])
    
    Login or Signup to reply.
  2. 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 "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 for="darkmode-toggle" id="toggle-label" />
        </div>
      );
    }
    
    

    Sandbox

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search