nextjs react recoil 在本地存储中持久保存值:初始页面加载状态错误
nextjs react recoil persist values in local storage: initial page load in wrong state
我有以下代码,
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 的后坐力。
所以,当 darkMode 值为 true 时,className 应该包含一个深色 class,对吗?但事实并非如此。我不知道这里出了什么问题。但是当我第一次刷新时它就不起作用,之后它就可以正常工作了。我也试过 darkMode === true 条件,但它仍然不起作用。您会看到样式化的 jsx,效果很好。这随 darkMode 值而变化,当我刷新时它会保留数据。但是当我检查时,我没有在第一个 div 中看到黑暗 class。另外,当我 console.log 的 darkMode 值时,我看到 true,但不包括黑暗 class。
也许这是一个愚蠢的错误,但我在这上面浪费了很多时间。那么我在这里做错了什么?
问题是在 SSR(服务器端呈现)期间没有可用的 localStorage
/Storage
对象。因此来自服务器的结果 html 始终将 darkMode
设置为 false
。这就是为什么你可以在 cosole 中看到水合作用步骤中不匹配的标记错误。
我假设在初始渲染(在水合步骤期间)中使用一些始终为 false
的状态来匹配 SSR'ed 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();
扩展@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])
现在创建一个原子效果来替换后坐力持续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]
})
我有以下代码,
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 的后坐力。 所以,当 darkMode 值为 true 时,className 应该包含一个深色 class,对吗?但事实并非如此。我不知道这里出了什么问题。但是当我第一次刷新时它就不起作用,之后它就可以正常工作了。我也试过 darkMode === true 条件,但它仍然不起作用。您会看到样式化的 jsx,效果很好。这随 darkMode 值而变化,当我刷新时它会保留数据。但是当我检查时,我没有在第一个 div 中看到黑暗 class。另外,当我 console.log 的 darkMode 值时,我看到 true,但不包括黑暗 class。
也许这是一个愚蠢的错误,但我在这上面浪费了很多时间。那么我在这里做错了什么?
问题是在 SSR(服务器端呈现)期间没有可用的 localStorage
/Storage
对象。因此来自服务器的结果 html 始终将 darkMode
设置为 false
。这就是为什么你可以在 cosole 中看到水合作用步骤中不匹配的标记错误。
我假设在初始渲染(在水合步骤期间)中使用一些始终为 false
的状态来匹配 SSR'ed 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();
扩展@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])
现在创建一个原子效果来替换后坐力持续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]
})