简体   繁体   中英

How do I make sure my first render with React has a value from localStorage?

My website has a light and dark theme. The default theme is light . If the user changes the theme to dark , it is saved to localStorage .

On the next visit/refresh at the root of my component tree, this code runs:

  useLayoutEffect(() => {
    let storedTheme = localStorage.getItem("theme");

    if (storedTheme === "light" || storedTheme === "dark") {

    // Redux action. Other components subscribe to the theme.
      setTheme(storedTheme);
    }
  }, [setTheme]);

Say the user chose the dark theme as their preference. It works fine. However, the first render will be the light theme. The second render will be the dark theme. This causes a light to dark flicker when visiting the site.

Is there a way to ensure my first render has the set value from localStorage ?

Implement theme saving to local storage as follows.

const [storedTheme, setStoredTheme] = useState(JSON.parse(localStorage.getItem("theme")) || "light");


useEffect(() => {
    localStorage.setItem("theme", JSON.stringify(storedTheme));
}, [storedTheme]);

And then in the App UI use method setStoredTheme() to set theme to "light" or "dark".

What worked in getting data to my first render was initializing the redux store like so:

if (typeof localStorage != "undefined") {
  initialState = {
    theme: localStorage.getItem("theme"),
  };
} else {
  initialState = {};
}

const store = createStore(reducers, initialState);

But it worsened the situation as the components no longer re-rendered on first load because the theme prop from redux never changed because the store was initialized with the user preference. So I went back to the old approach with in the original question above.

The flicker happens because I am using Javascript to control the theme in my react app. I am also using Gatsby which generates a static version of my site that loads before my react app.

Loading the site goes like this:

  1. Eg User with dark theme preference visits. They see static content without any Javascript while JS bundles download in the background.

  2. Since light theme is the default, the static version of the site will be light.

  3. Javascript loads and React's first render happens and the redux store is intialized. The root component gets the theme from localStorage and the tree re-renders with the dark theme.

The flicker happens because of the events between 2 and 3.

I didn't manage to solve the issue completely. But I managed to alleviate the flicker to be much more unnoticable by enabling Gatsby's service worker. The worker caches Javascript bundles for next site visit, and loading Javascript from disk means the flicker will linger for a shorter amount of time as the JS bundles are already available for execution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM