如何修复在Next.js中使用localStorage时的水合错误?

uqjltbpv  于 2023-05-17  发布在  其他
关注(0)|答案(1)|浏览(176)

我试图在Next.js项目中的localStorage中持久化上下文,但当我刷新页面时出现了一个水合错误。
我该如何解决这个问题?

type AppState = {
  name: string;
  salary: number;
  info: {
    email: string;
    department: string;
  };
};

interface Props {
  children: ReactNode;
}

const initialState: AppState = {
  name: "osamu1908",
  salary: 2000,
  info: {
    email: "dynamic@gmail.com",
    department: "software developer",
  },
};

export const AppState = createContext<AppState>(initialState);
export const AppStateSet = createContext<Dispatch<SetStateAction<AppState>>>(
  () => {}
);

const initalFromLocalStorage = () => {
  if (typeof window !== "undefined") {
    const result = localStorage.getItem("state");

    if (result) {
      return JSON.parse(result) as AppState;
    }
  }

  return initialState;
};

const AppStateProvider = ({ children }: Props) => {
  const [state, setState] = useState<AppState>(initalFromLocalStorage());

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

  return (
    <AppState.Provider value={state}>
      <AppStateSet.Provider value={setState}>{children}</AppStateSet.Provider>
    </AppState.Provider>
  );
};

export default AppStateProvider;
yuvru6vn

yuvru6vn1#

客户端和服务器上的初始渲染应该相同。任何时候你违反这个规则,你会得到一个hydration错误,就像在你的情况下,在服务器上没有localStorage,它存在于浏览器上,导致不同的初始状态。
您应该将示例化主题的逻辑移动到useEffect中:

const AppStateProvider = ({ children }: Props) => {
  const [state, setState] = useState<AppState | null>(null);

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

  useEffect(() => {
    setState(initalFromLocalStorage());
  }, []);

  // This is so you don't show anything before the context is fully set. You could also set a loader or something.
  if (!state) {
    return null;
  }

  return (
    <AppState.Provider value={state}>
      <AppStateSet.Provider value={setState}>{children}</AppStateSet.Provider>
    </AppState.Provider>
  );
};

此时,您可以简化initalFromLocalStorage如下:

const initalFromLocalStorage = () => {
  const result = localStorage.getItem("state");

  if (result) {
    return JSON.parse(result) as AppState;
  }

  return initialState;
};

相关问题