如何创建使用Cookie、支持服务端渲染(SSR)的持久化状态Hook
问题根因
你遇到的服务端读不到Cookie、渲染不一致的核心原因是:react-cookie 的 CookiesProvider 在服务端渲染场景下无法自动获取请求头中的Cookie,需要你手动将服务端拿到的Cookie值注入到Provider中,才能让服务端运行的自定义Hook读取到正确的Cookie值。
解决方案
步骤1:修改_app.tsx,全局注入服务端Cookie
给自定义App添加getInitialProps,在服务端阶段提取请求头中的Cookie,传递给CookiesProvider作为初始值:
// src/pages/_app.tsx import App, { AppProps, AppInitialProps } from 'next/app'; import { ThemeProvider } from 'styled-components'; import { Provider as AuthProvider } from 'next-auth/client'; import { CookiesProvider } from 'react-cookie'; import DialogContainer from '../components/shared/DialogContainer'; import { ToastsContainer } from '../components/shared/ToastsContainer'; import { ThemeContextProvider, useTheme } from '../hooks/theme'; import { DialogContextProvider } from '../hooks/dialog'; import { NormalTimerContextProvider } from '../hooks/timer/normal'; import { ToastContextProvider, useToast } from '../hooks/toast'; import { SoundContextProvider } from '../hooks/sound'; import GlobalStyle from '../styles/global'; const AppWithProviders: React.FC<AppProps & { cookies?: string }> = ({ Component, pageProps, cookies }) => { const { theme } = useTheme(); const { messages } = useToast(); return ( <AuthProvider session={pageProps.session}> {/* 把服务端拿到的cookie传给CookiesProvider */} <CookiesProvider cookies={cookies}> <SoundContextProvider> <ThemeProvider theme={theme}> <DialogContextProvider> <NormalTimerContextProvider> <Component {...pageProps} /> <GlobalStyle /> <DialogContainer /> <ToastsContainer messages={messages} /> </NormalTimerContextProvider> </DialogContextProvider> </ThemeProvider> </SoundContextProvider> </CookiesProvider> </AuthProvider> ); }; const MyApp: React.FC<AppProps> = (props) => ( <ThemeContextProvider> <ToastContextProvider> <AppWithProviders {...props} /> </ToastContextProvider> </ThemeContextProvider> ); // 新增getInitialProps获取服务端Cookie MyApp.getInitialProps = async (appContext) => { const appProps = await App.getInitialProps(appContext); const { req } = appContext.ctx; let cookies = ''; // 仅服务端运行时提取Cookie if (req) { cookies = req.headers.cookie || ''; } return { ...appProps, cookies, }; }; export default MyApp;
步骤2:优化usePersistedState的Cookie配置
给setCookie添加path: '/'配置,确保Cookie全站可访问,避免路径限制导致读不到:
// src/hooks/persistedState.tsx import { Dispatch, SetStateAction, useEffect, useState } from 'react'; import { useCookies } from 'react-cookie'; type Response<T> = [T, Dispatch<SetStateAction<T>>]; export default function usePersistedState<T = any>(key: string, initialState: T): Response<T> { const [cookies, setCookie] = useCookies([key]); const [state, setState] = useState<T>(() => { if (cookies[key]) return cookies[key]; return initialState; }); useEffect(() => { // 新增path配置,保证全站生效 setCookie(key, state, { path: '/' }); }, [key, setCookie, state]); return [state, setState]; }
验证
修改完成后刷新页面,服务端渲染阶段就能正确读取到Cookie中存储的主题值,不会再出现服务端渲染浅色、客户端瞬间切深色的闪烁问题,也不会有hydration不匹配的报错。
内容的提问来源于stack exchange,提问作者Bruno Mello
相关产品推荐
相关产品推荐

