Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

useDarkMode resets #512

Open
vhpoet opened this issue Feb 23, 2024 · 7 comments · May be fixed by #557 or #636
Open

useDarkMode resets #512

vhpoet opened this issue Feb 23, 2024 · 7 comments · May be fixed by #557 or #636
Labels
bug Something isn't working

Comments

@vhpoet
Copy link

vhpoet commented Feb 23, 2024

Describe the bug

This is causing the isDarkMode to reset to the OS setting on every refresh. For some reason it works on production though.

To Reproduce

Use it in local dev with next.js where the OS is set to dark mode.

Expected behavior

I expect my choice to be persisted.

Additional context

No response

@vhpoet vhpoet added the bug Something isn't working label Feb 23, 2024
@smith558
Copy link

smith558 commented Feb 26, 2024

useDarkMode currently not working on Linux either (Ubuntu), resets on every page load (no persistence of mode preference). Using Next.js as well. For me it doesn't work in the production either.

@refeals
Copy link

refeals commented Mar 20, 2024

Same thing here. Did a few tests, my guess is that the function in useIsomorphicLayoutEffect (that runs on refresh) is checking the wrong stuff (L72) before setting the local storage variable to isDarkOS (which in this case will always be true).

I think this there's a specific case that is not treated: when no value is set on local storage, useLocalStorage defaults to a true/false value based on the OS settings, instead of undefined. That ripples to useIsomorphicLayoutEffect and incorrectly checks the values and then sets the OS value.

I'm not sure what's the best way to fix this problem, since it seems to be related to other hooks and the way they handle the default values.

@juliencrn
Copy link
Owner

Hi all, thanks for the bug report, feel free to open a PR, I will check it in priority

@refeals
Copy link

refeals commented Mar 25, 2024

Just created #557

The change in useLocalStorage may possibly break other hooks, but I tried to make it consistent. It still does not return undefined when the key does not exist, but now it creates the key/value in local storage when the hook is called. That asserts the local storage as the source of truth.

And that should fix the useDarkMode inconsistency too, so I removed the useIsomorphicLayoutEffect since it's now unnecessary - the isDarkMode variable is always reflecting the correct data in local storage and it does not update on refresh.

@gregmsanderson
Copy link

Ah, been finding this too. The chosen option is lost on a refresh as localStorage is reset. That explains why!

@pavitra-infocusp
Copy link

pavitra-infocusp commented Nov 11, 2024

I have tripped on this issue too.


Fixed by copying the hook and commenting out useIsomorphicLayoutEffect

import {
    useIsomorphicLayoutEffect,
    useLocalStorage,
    useMediaQuery,
  } from 'usehooks-ts'
  
  const COLOR_SCHEME_QUERY = '(prefers-color-scheme: dark)'
  const LOCAL_STORAGE_KEY = 'usehooks-ts-dark-mode'
  
  type DarkModeOptions = {
    defaultValue?: boolean
    localStorageKey?: string
    initializeWithValue?: boolean
  }
  
  type DarkModeReturn = {
    isDarkMode: boolean
    toggle: () => void
    enable: () => void
    disable: () => void
    set: (value: boolean) => void
  }
  
  export function useDarkMode(options: DarkModeOptions = {}): DarkModeReturn {
    const {
      defaultValue,
      localStorageKey = LOCAL_STORAGE_KEY,
      initializeWithValue = true,
    } = options
  
    const isDarkOS = useMediaQuery(COLOR_SCHEME_QUERY, {
      initializeWithValue,
      defaultValue,
    })
    const [isDarkMode, setDarkMode] = useLocalStorage<boolean>(
      localStorageKey,
      defaultValue ?? isDarkOS ?? false,
      { initializeWithValue },
    )
  
    // FIX: https://github.com/juliencrn/usehooks-ts/issues/512
    // Update darkMode if os prefers changes
    // useIsomorphicLayoutEffect(() => {
    //   if (isDarkOS !== isDarkMode) {
    //     setDarkMode(isDarkOS)
    //   }
    // }, [isDarkOS])
  
    return {
      isDarkMode,
      toggle: () => {
        setDarkMode(prev => !prev)
      },
      enable: () => {
        setDarkMode(true)
      },
      disable: () => {
        setDarkMode(false)
      },
      set: value => {
        setDarkMode(value)
      },
    }
  }

@iavorskyi
Copy link

Had the same problem. Fixed with this approach too. Looking forward to this fix will be delivered to the main.

I have tripped on this issue too.

Fixed by copying the hook and commenting out useIsomorphicLayoutEffect

import {
    useIsomorphicLayoutEffect,
    useLocalStorage,
    useMediaQuery,
  } from 'usehooks-ts'
  
  const COLOR_SCHEME_QUERY = '(prefers-color-scheme: dark)'
  const LOCAL_STORAGE_KEY = 'usehooks-ts-dark-mode'
  
  type DarkModeOptions = {
    defaultValue?: boolean
    localStorageKey?: string
    initializeWithValue?: boolean
  }
  
  type DarkModeReturn = {
    isDarkMode: boolean
    toggle: () => void
    enable: () => void
    disable: () => void
    set: (value: boolean) => void
  }
  
  export function useDarkMode(options: DarkModeOptions = {}): DarkModeReturn {
    const {
      defaultValue,
      localStorageKey = LOCAL_STORAGE_KEY,
      initializeWithValue = true,
    } = options
  
    const isDarkOS = useMediaQuery(COLOR_SCHEME_QUERY, {
      initializeWithValue,
      defaultValue,
    })
    const [isDarkMode, setDarkMode] = useLocalStorage<boolean>(
      localStorageKey,
      defaultValue ?? isDarkOS ?? false,
      { initializeWithValue },
    )
  
    // FIX: https://github.com/juliencrn/usehooks-ts/issues/512
    // Update darkMode if os prefers changes
    // useIsomorphicLayoutEffect(() => {
    //   if (isDarkOS !== isDarkMode) {
    //     setDarkMode(isDarkOS)
    //   }
    // }, [isDarkOS])
  
    return {
      isDarkMode,
      toggle: () => {
        setDarkMode(prev => !prev)
      },
      enable: () => {
        setDarkMode(true)
      },
      disable: () => {
        setDarkMode(false)
      },
      set: value => {
        setDarkMode(value)
      },
    }
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
7 participants