4

我有以下代码,

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 和recoil-persist。那么,当darkMode 值为true 时,className 应该包含一个dark 类,对吗?但事实并非如此。我不知道这里有什么问题。但是当我第一次刷新时它不起作用,之后它就可以正常工作了。我也尝试了 darkMode === true 条件,但它仍然不起作用。您会看到样式化的 jsx,效果很好。这会随着 darkMode 值的变化而变化,当我刷新它时,它会保留数据。但是当我检查时,我在第一个 div 中看不到黑暗类。此外,当我 console.log 的 darkMode 值时,我看到了 true,但不包括 dark 类。

这是沙盒链接

也许这是一个愚蠢的错误,但我在这上面浪费了很多时间。那么我在这里做错了什么?

4

2 回答 2

6

问题是在 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();

代码框链接

于 2021-06-24T09:11:25.173 回答
2

扩展@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)
}

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

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

现在创建一个原子效果来替换 recoil-persist 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))
}

现在在你的 atom 中使用这个新函数。

export const darkModeAtom = atom({
  key: "darkMode",
  default: false,
  effects_UNSTABLE: [persistAtomEffect]
})
于 2021-12-23T09:12:44.517 回答