Jack Pollock
Jack Pollock

Reputation: 334

Unable to override undefined value from localStorage

I'm trying to create a dark mode toggle in React and TailwindCSS for my web page, and have been following this tutorial: https://omerduraker.medium.com/dark-and-light-mode-using-react-tailwind-css-58bb8f988080.

The tutorial worked fine, however when I tried to change the default behaviour to default to dark mode rather than light mode, I have been running into issues, I presume with states not updating? The tutorial uses localStorage to save the chosen colour mode preference, however when you first visit the page, as there is no localStorage value present, it only retrieves the value 'undefined' meaning that the application defaults to light mode. The issue primarily pertains to behaviour on first loading of the website, once you have a valid localStorage value, it works fine!

Here is the code I have been trying to use to override this behaviour, but while I can override the page to dark mode, my toggle switch is staying stuck in the position it would be if the page were in light mode. I have provided a screenshot highlighting the issue below. Can anyone see what I'm doing wrong? Thanks!

This should show a white moon icon, not a black sun. enter image description here

Looking in React developer tools, it appears the state is not updating. The state highlighted should be true, but is unfortunately showing false for some reason.

enter image description here

useDarkMode.tsx:

import React, { useEffect, useState } from "react";


export default function useDarkMode() {
    const [theme, setTheme] = useState(localStorage.theme);
    if (typeof theme === 'undefined') {
        setTheme('dark');
    }
    const colourTheme = theme === 'dark' ? 'light' : ('light' ? 'dark' : 'light');

    useEffect(() => {
        const root = window.document.documentElement;
        root.classList.remove(colourTheme);
        root.classList.add(typeof theme === 'undefined' ? 'dark' : theme);

        localStorage.setItem('theme', typeof theme === 'undefined' ? 'dark' : theme);
    }, [theme, colourTheme]);

    return [colourTheme, setTheme] as any;
}

nav.tsx

export default function Nav(props: any) {
    const [colourtheme, setTheme] = useDarkMode();
    const [isDarkMode, setDarkMode] = useState(
        colourtheme === "dark" ? true : false
    );
    
    const dmProps = {
        colourtheme: colourtheme,
        setTheme: setTheme,
        isDarkMode: isDarkMode,
        setDarkMode: setDarkMode,
    };

....

<Switcher className="flex flex-col ml-2 mr-1.5 md:flex-row md:space-x-8 md:mt-0" functions={dmProps} />

switcher.tsx

import React, { useState } from 'react';
import { DarkModeSwitch } from 'react-toggle-dark-mode';

export default function Switcher(props: any) {

    function toggleDarkMode(checked: boolean):void {
        props.functions.setTheme(props.functions.colourtheme);
        props.functions.setDarkMode(checked);
    }

    return (
        <div className={`flex flex-col items-center w-auto ${props.className}`}>
            <DarkModeSwitch
            style={{ }}
            checked={props.functions.isDarkMode}
            onChange={toggleDarkMode}
            size={25}
            />
        </div>
    );
};

Upvotes: 0

Views: 63

Answers (1)

Ed Lucas
Ed Lucas

Reputation: 7305

I'm not sure where the localstate object used as the default in your useState is coming from. Ordinarily, you would use localstorage.getItem('theme') to retrieve this value. This getItem function call should not return undefined, since it defaults to null if the key (e.g. 'theme') is not found.

See: https://developer.mozilla.org/en-US/docs/Web/API/Storage/getItem

Also, you may want to refactor your double ternary:

const colourTheme = theme === 'dark' ? 'light' : ('light' ? 'dark' : 'light');

The second ternary (in parenthesis) will always return 'dark', since the condition, 'light' is equivalent to true.

Upvotes: 2

Related Questions