Next.js проблема использования локального хранилища с SSR

#reactjs #react-hooks #next.js #server-side-rendering

Вопрос:

У меня есть следующий пользовательский хук, который хранит данные в локальном хранилище:

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

export const useLocalStorage = (key, initialValue) => {
  const initialize = (key) => {
    try {
      const item = localStorage.getItem(key);
      if (item amp;amp; item !== "undefined") {
        return JSON.parse(item);
      }

      localStorage.setItem(key, JSON.stringify(initialValue));
      return initialValue;
    } catch {
      return initialValue;
    }
  };

  const [state, setState] = useState(() => initialize(key)); // problem is here

  const setValue = useCallback(
    (value) => {
      try {
        const valueToStore = value instanceof Function ? value(storedValue) : value;
        setState(valueToStore);
        localStorage.setItem(key, JSON.stringify(valueToStore));
      } catch (error) {
        console.log(error);
      }
    },
    [key, setState]
  );

  const remove = useCallback(() => {
    try {
      localStorage.removeItem(key);
    } catch {
      console.log(error);
    }
  }, [key]);

  return [state, setValue, remove];
};


 

Это показывает следующую проблему, которую я погуглил, и, похоже, это связано с тем, что Nextjs пытается запустить код на стороне сервера, и нет доступного объекта окна.

введите описание изображения здесь

Проблема, похоже, исходит из строки, в которой я пытаюсь инициализировать сохраненные данные:

 const [state, setState] = useState(() => initialize(key));
 

Я попытался упаковать эту логику в эффект использования, чтобы она работала только на стороне клиента, но у меня получился бесконечный цикл, который я не смог решить.

Ответ №1:

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

export const useLocalStorage = (key, initialValue) => {
  const initialize = (key) => {
    try {
      const item = localStorage.getItem(key);
      if (item amp;amp; item !== "undefined") {
        return JSON.parse(item);
      }

      localStorage.setItem(key, JSON.stringify(initialValue));
      return initialValue;
    } catch {
      return initialValue;
    }
  };

  const [state, setState] = useState(null); // problem is here

  // solution is here....
  useEffect(()=>{
    setState(initialize(key));
  },[]);

  const setValue = useCallback(
    (value) => {
      try {
        const valueToStore = value instanceof Function ? value(storedValue) : value;
        setState(valueToStore);
        localStorage.setItem(key, JSON.stringify(valueToStore));
      } catch (error) {
        console.log(error);
      }
    },
    [key, setState]
  );

  const remove = useCallback(() => {
    try {
      localStorage.removeItem(key);
    } catch {
      console.log(error);
    }
  }, [key]);

  return [state, setValue, remove];
};
 

window,localStorage,sessionStorage,etc.. не определены на сервере, поэтому доступ к ним на сервере приведет к ошибке. Используйте useEffect , чтобы убедиться, что этот код будет выполняться на стороне клиента.

Комментарии:

1. React Hook useEffect has missing dependencies: 'initialize' and 'key'. Поэтому, когда я добавляю метод initialzie в depenedncy, он говорит, что метод initialize должен использовать обратный вызов, поэтому я сделал это с помощью этого. Когда я добавляю initialValue в массив зависимостей useCallback, я получаю бесконечный цикл.

2. Ничего не добавляйте

3. Является ли хорошей практикой исключить зависимость?

4. @J. Doe useEffect полностью работает на стороне клиента, поэтому вам не нужно добавлять никаких зависимостей.

5. reactjs.org/docs/…

Ответ №2:

Возможно, переместите инициализацию внутрь эффекта использования (однако состояние использования должно быть сохранено снаружи).

Внутри эффекта использования вы инициализируете только в том случае, если typeof window !== "undefined"

Комментарии:

1. useEffect запускается только на стороне клиента, поэтому проверка окна не требуется. И проблема не в этом. Смотрите комментарий к ответу Рахула.

2. В next.js, он может работать на сервере во время SSR, например, когда вы запускаете что-то локально, нет?

3. useEffect имеет механизм csr(рендеринга на стороне клиента), он не отличается, если вы используете предварительный рендеринг ssg или ssr

4. Хорошо, теперь я понял, спасибо за объяснение