nextjs react recoil在本地存储中持久保存值:初始页面加载处于错误状态

5uzkadbs  于 2023-03-18  发布在  React
关注(0)|答案(3)|浏览(253)

我有下面的代码,

const Layout: React.FC<LayoutProps> = ({ children }) => {
    const darkMode = useRecoilValue(darkModeAtom)
    
    console.log('darkMode: ', darkMode)
    return (
        <div className={`max-w-6xl mx-auto my-2 ${darkMode ? 'dark' : ''}`}>
            <Nav />
            {children}
            <style jsx global>{`
                body {
                    background-color: ${darkMode ? '#12232e' : '#eefbfb'};
                }
            `}</style>
        </div>
    )
}

我在recoil-persist中使用recoil。因此,当darkMode值为true时,className应该包含一个dark类,对吗?但它没有。我不知道这里出了什么问题。但它在我第一次刷新时不起作用,之后它工作正常。我也尝试了darkMode === true条件,它仍然不起作用。您可以看到样式化的jsx,这很好用。它随darkMode值而变化,当我刷新它时,它保持数据。但当我检查时,我在第一个div中没有看到dark类。另外,当我console.log darkMode值时,我看到true,但dark类不包括在内。
这是sandbox link
也许这是个愚蠢的错误,但我在这上面浪费了很多时间,我到底做错了什么?

uxh89sit

uxh89sit1#

问题是在SSR(服务器端渲染)期间没有localStorage/Storage对象可用。因此,来自服务器的结果html总是将darkMode设置为false。这就是为什么您可以在水合步骤的cosole中看到不匹配的标记错误。
我假设在初始渲染时(在水合步骤中)使用一些总是false的状态来匹配SSR'艾德html,但以后将使用实际的darkMode值。

// themeStates.ts
import * as React from "react";
import { atom, useRecoilState } from "recoil";
import { recoilPersist } from "recoil-persist";

const { persistAtom } = recoilPersist();

export const darkModeAtom = atom<boolean>({
  key: "darkMode",
  default: false,
  effects_UNSTABLE: [persistAtom]
});

export function useDarkMode() {
  const [isInitial, setIsInitial] = React.useState(true);
  const [darkModeStored, setDarkModeStored] = useRecoilState(darkModeAtom);

  React.useEffect(() => {
    setIsInitial(false);
  }, []);

  return [
    isInitial === true ? false : darkModeStored,
    setDarkModeStored
  ] as const;
}

内部组件是这样使用它的:

// Layout.tsx
  const [darkMode] = useDarkMode();
// Nav.tsx
  const [darkMode, setDarkMode] = useDarkMode();

codesandbox link

1u4esq0p

1u4esq0p2#

在@aleksxor解决方案上扩展,您可以执行useEffect一次,如下所示。
首先创建一个原子来处理SSR完成状态,并创建一个方便的函数来设置它。

import { atom, useSetRecoilState } from "recoil"

const ssrCompletedState = atom({
  key: "SsrCompleted",
  default: false,
})

export const useSsrComplectedState = () => {
  const setSsrCompleted = useSetRecoilState(ssrCompletedState)
  return () => setSsrCompleted(true)
}

然后在代码中添加钩子,确保它是反冲提供程序的内部组件。

const setSsrCompleted = useSsrComplectedState()
useEffect(setSsrCompleted, [setSsrCompleted])

现在创建一个原子效果来替换反冲持久persistAtom

import { AtomEffect } from "recoil"
import { recoilPersist } from "recoil-persist"

const { persistAtom } = recoilPersist()

export const persistAtomEffect = <T>(param: Parameters<AtomEffect<T>>[0]) => {
  param.getPromise(ssrCompletedState).then(() => persistAtom(param))
}

现在在原子中使用这个新函数。

export const darkModeAtom = atom({
  key: "darkMode",
  default: false,
  effects_UNSTABLE: [persistAtomEffect]
})
h7appiyu

h7appiyu3#

这对我很有效。
1.复制这些代码。
https://recoiljs.org/docs/guides/atom-effects/#local-storage-persistence
1.将本地存储替换为nookies

import { parseCookies, setCookie, destroyCookie } from "nookies";

const cookies = parseCookies();
const localStorageEffect =
  (key) =>
  ({ setSelf, onSet }) => {
    const savedValue = cookies[key];
    if (savedValue != null) {
      setSelf(JSON.parse(savedValue));
    }

    onSet((newValue, _, isReset) => {
      isReset
        ? destroyCookie(null, key)
        : setCookie(null, key, JSON.stringify(newValue));
    });
  };

1.修正了水合问题。
https://stackoverflow.com/a/72318597/5451474

相关问题