javascript 使用样式化组件和React上下文的水合错误

brgchamk  于 2023-01-16  发布在  Java
关注(0)|答案(2)|浏览(135)

我有下一个错误。我有两个变体的主题在我的应用程序-黑暗和光明。
下面是沙箱-https://codesandbox.io/p/sandbox/trusting-mestorf-1rc2xv?selection=%5B%7B%22endColumn%22%3A7%2C%22endLineNumber%22%3A11%2C%22startColumn%22%3A7%2C%22startLineNumber%22%3A11%7D%5D&file=%2Fpages%2Fboard%2F%5Bslug%5D.tsx

    • 主题上下文. ts**
export const ThemeContext = createContext<{
  theme: AppThemeInterface,
  setTheme: Dispatch<SetStateAction<AppThemeInterface>>,
  // eslint-disable-next-line no-unused-vars
  updateThemeKey: (newThemeKey: ThemeKeys) => void,
}>({
  theme: getTheme(ThemeKeys.DARK) // here is just object with props from interface,
  setTheme: () => null,
  updateThemeKey: () => null,
});
    • AppThemeProvider. ts**这里我从LocalStorage接收theme_key,并通过该键获取主题,然后将其值设置为ThemeContext
export const AppThemeProvider = ({ children }: AppThemeProviderProps) => {
  const [currentThemeKey, setCurrentThemeKey] = useLocalStorage<ThemeKeys>('theme_key', ThemeKeys.DARK);
  const [theme, setTheme] = useState<AppThemeInterface>(getTheme(currentThemeKey));

  const updateThemeKey = (value: ThemeKeys) => {
    setCurrentThemeKey(value);
  };

  return (
    <ThemeContext.Provider value={
      { theme, setTheme, updateThemeKey }
    }>
      <ThemeProvider theme={theme}>
        {children}
      </ThemeProvider>
    </ThemeContext.Provider>
  );
};

使用本地存储挂钩

import { useState } from 'react';

export const useLocalStorage = <T>(key: string, initialValue: T) => {
  const [value, setValue] = useState<T>(() => {
    if (typeof window === 'undefined') {
      return initialValue;
    }
    const lcItem = localStorage.getItem(key);
    const endVal = lcItem ? JSON.parse(lcItem) : initialValue;
    localStorage.setItem(key, JSON.stringify(endVal));
    return endVal;
  });

  const saveValue = (value: T): void => {
    localStorage.setItem(key, JSON.stringify(value));
  };

  const onChangeValue = (value: T): void => {
    saveValue(value);
    setValue(value);
  };

  return [value, onChangeValue] as const;
};

使用这种设置,当LocalStorage中的键与dark不同时,如果LocalStorage中的键为light,则会出现下一个错误

**

  • 属性className不匹配。服务器:
    **

这是因为首先它被渲染为黑暗的主题,但随后它又重新渲染为光明的主题。
我不知道如何使它正确渲染

ny6fqffe

ny6fqffe1#

export const ThemeContext = createContext<{
  theme: AppThemeInterface,
  setTheme: Dispatch<SetStateAction<AppThemeInterface>>,
  // eslint-disable-next-line no-unused-vars
  updateThemeKey: (newThemeKey: ThemeKeys) => void,
}>({
  theme: getTheme(ThemeKeys.DARK) // here is just object with props from interface,
  setTheme: () => null,
  updateThemeKey: () => null,
});
qjp7pelc

qjp7pelc2#

问题

状态value将始终在服务器上使用initialValue进行计算,但是,在客户端上计算时,将使用localStorage中的密钥(因为window是在客户端上定义的)。例如,如果initialValue在服务器上是"亮的",但是在localStorage中可能是"暗的",你会得到一个水合错误,因为状态中的value不匹配。

溶液

由于localStorage仅用于客户端,因此您需要在useEffect中计算主题值。简而言之,useEffect仅在客户端运行,因此您将始终在服务器和客户端使用initialValue,但仅在客户端上,一旦useEffect在浏览器中运行,它将重新计算。

演示

下面是一个非常简单的demo,它基于localStorage中的值派生主题。

缺点

如果值不是initialValue,则使用客户端存储的值将导致UI Flink 。您可以通过将值移动到可在服务器端访问的外部资源来避免此 Flink 。例如,将此值保存到可使用下一个生命周期方法之一访问的数据库:getServerSidePropsgetInitialProps。但这带来了另一个障碍:如何知道数据库中的哪个值属于当前用户?无论值是从客户端还是从服务器检索的,当使用它来动态设置全局值时,您都会遇到障碍。

相关问题