如何在Next.js中实现useLocalStorage钩子?

m1m5dgzv  于 2023-03-08  发布在  其他
关注(0)|答案(3)|浏览(147)

我正在尝试在next.js中创建一个能够适应页面刷新的useState的替代品。
其中一个可能的解决方案是使用window.localStorage来保存和检索状态,这样即使在页面刷新之后状态也会保持不变。
我发现了下面的ReactJS https://usehooks.com/useLocalStorage/useLocalStorage钩子实现

function useLocalStorage(key, initialValue) {
  // State to store our value
  // Pass initial state function to useState so logic is only executed once
  const [storedValue, setStoredValue] = useState(() => {
    if (typeof window === "undefined") {
      return initialValue;
    }
    try {
      // Get from local storage by key
      const item = window.localStorage.getItem(key);
      // Parse stored json or if none return initialValue
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      // If error also return initialValue
      console.log(error);
      return initialValue;
    }
  });
  // Return a wrapped version of useState's setter function that ...
  // ... persists the new value to localStorage.
  const setValue = (value) => {
    try {
      // Allow value to be a function so we have same API as useState
      const valueToStore =
        value instanceof Function ? value(storedValue) : value;
      // Save state
      setStoredValue(valueToStore);
      // Save to local storage
      if (typeof window !== "undefined") {
        window.localStorage.setItem(key, JSON.stringify(valueToStore));
      }
    } catch (error) {
      // A more advanced implementation would handle the error case
      console.log(error);
    }
  };
  return [storedValue, setValue];
}

但是,当我在NextJS中使用它时,它生成了以下错误:
未捕获的错误:水合失败,因为初始UI与服务器上呈现的内容不匹配
经过一番搜索,我发现window对象不存在于(Next.js)服务器端,这可能是错误(Window is not defined in Next.js React app)的原因。一个可能的解决方案是使用useEffect钩子来保护window的使用,该钩子只在客户端运行。
我当前的useLocalStorage钩子实现是

function useLocalStorage<T>(key: string, defaultValue: T): [T, Dispatch<SetStateAction<T>>] {
  const [value, setValue] = useState<T>(defaultValue);

  useEffect(() => {
    try {
      const item = window.localStorage.getItem(key);
      setValue(item ? JSON.parse(item) : defaultValue);
    }
    catch (error) {
      setValue(defaultValue);
    }
    
  }, [key, defaultValue]);

  useEffect(() => {
    window.localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue];
};

然而,这一次钩子并不总是像预期的那样工作,因为useEffect回调的执行顺序没有得到保证,结果,有时状态会丢失。
我想知道在NextJS中什么是正确的实现,并了解我的代码逻辑在哪里失败。

2w3rbyxf

2w3rbyxf1#

下面是一个useLocalStorage钩子,它与next.js一起工作

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

export const useLocalStorage = <S>(
  key: string,
  initialState?: S | (() => S)
): [S, React.Dispatch<React.SetStateAction<S>>] => {
  const [state, setState] = useState<S>(initialState as S);
  useDebugValue(state);

  useEffect(() => {
    const item = localStorage.getItem(key);
    if (item) setState(parse(item));
  }, []);

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(state));
  }, [state]);

  return [state, setState];
};

const parse = (value: string) => {
  try {
    return JSON.parse(value);
  } catch {
    return value;
  }
};
jv4diomz

jv4diomz2#

Markos的回答总是让我在刷新时重置状态,我添加了一个小调整,解决了这个问题:

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

const useLocalStorage = <S>(
  key: string,
  initialState?: S | (() => S)
): [S, React.Dispatch<React.SetStateAction<S>>] => {
  const [state, setState] = useState<S>(initialState as S)
  useDebugValue(state)

  useEffect(() => {
    const item = localStorage.getItem(key)
    if (item) setState(parse(item))
  }, [])

  useEffect(() => {
    if (state !== initialState) {
      localStorage.setItem(key, JSON.stringify(state))
    }
  }, [state])

  return [state, setState]
}

const parse = (value: string) => {
  try {
    return JSON.parse(value)
  } catch {
    return value
  }
}

export default useLocalStorage
ee7vknir

ee7vknir3#

这对我很有效:Next.js use localstorage problem with SSR
......而且它不会像@MarkosTh09的解决方案那样在每次刷新时重置值。

相关问题