我试图在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;

推荐答案

客户端和服务器上的初始呈现应该相同.任何时候违反这一规则,您都会得到一个hydration错误,就像您的例子一样,服务器上没有localStorage,而浏览器上有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;
};

Javascript相关问答推荐

有没有方法在Angular中的bowser选项卡之间移动HTML?

Python类实现的JavaScript吊坠是什么样子的?

Toast函数找不到其dis

JavaScript:循环访问不断变化的数组中的元素

从外部访问组件变量-Vueejs

如何访问react路由v6加载器函数中的查询参数/搜索参数

从实时数据库(Firebase)上的子类别读取数据

fs. writeFile()vs fs.writeFile()vs fs.appendFile()

为什么当我解析一个promise时,输出处于挂起状态?

用JavaScript复制C#CRC 32生成器

jQuery s data()[storage object]在vanilla JavaScript中?'

我创建了一个创建对象的函数,我希望从该函数创建的对象具有唯一的键.我怎么能做到这一点?

这个值总是返回未定义的-Reaction

用于在路径之间移动图像的查询

检索相加到点的子项

在验证和提交表单后使用useNavigate()进行react 重定向,使用带有加载器和操作的路由

使用RxJS from Event和@ViewChild vs KeyUp事件和RxJS主题更改输入字段值

JavaScript&;Reaction-如何避免在不使用字典/对象的情况下出现地狱?

有没有办法通过使用不同数组中的值进行排序

使用createBrowserRoutVS BrowserRouter的Reaction路由