SSR场景下如何延迟Redux Store初始化至客户端Hydration完成后?
解决SSR/SSG下Redux持久化导致的Hydration错误(延迟加载客户端状态)
核心思路:先使用服务端注入的初始状态完成Hydration,保证客户端初始UI与服务端完全匹配,待Hydration完成后再延迟加载localStorage中的状态并更新Store。
1. 修改客户端入口文件(_default.page.client.tsx)
import { hydrateRoot } from 'react-dom/client' import { Provider } from 'react-redux' import { createStore } from './store' import { loadState, saveState } from './localStorage' import App from './App' // 1. 优先使用服务端传递的初始状态,确保与服务端渲染UI一致 const serverInitialState = window.__INITIAL_STATE__ // vite-plugin-ssr会自动注入该全局变量 const store = createStore(serverInitialState) // 保持状态订阅保存逻辑不变 store.subscribe(() => { saveState(store.getState()) }) // 2. 执行Hydration,并等待完成后延迟恢复本地状态 const root = hydrateRoot( document.getElementById('root')!, <Provider store={store}> <App /> </Provider> ) // Hydration完成后触发状态恢复 root.then(() => { setTimeout(() => { const localSavedState = loadState() if (localSavedState) { // 方式1:通过自定义action合并本地状态(更可控) store.dispatch({ type: 'LOCAL_STATE_RESTORE', payload: localSavedState }) // 方式2:Redux-Toolkit可调用对应slice的恢复action // store.dispatch(userSlice.actions.restoreUser(localSavedState.user)) } }, 500) // 延迟0.5秒执行 })
2. 在Redux Store中处理状态恢复逻辑
如果使用Redux-Toolkit,以用户模块为例,在slice中添加恢复状态的action:
// store/slices/userSlice.ts import { createSlice } from '@reduxjs/toolkit' const userSlice = createSlice({ name: 'user', initialState: { code: '' }, reducers: { restoreUser: (state, action) => { // 合并本地状态,避免覆盖服务端预取数据 return { ...state, ...action.payload } } } }) export const { restoreUser } = userSlice.actions export default userSlice.reducer
如果是传统Redux,在reducer中添加对应case:
// store/reducers/index.ts const rootReducer = (state, action) => { switch (action.type) { case 'LOCAL_STATE_RESTORE': // 仅更新需要持久化的字段 return { ...state, user: { ...state.user, ...action.payload.user } } default: return state } }
3. 确保服务端正确传递初始状态
在服务端渲染入口(如pages/index/index.page.server.tsx)中,将Store状态注入页面上下文:
import { renderToString } from 'react-dom/server' import { Provider } from 'react-redux' import { createStore } from '../../store' import App from '../../App' export async function render() { const store = createStore() // 服务端预取数据(如有需要) // await store.dispatch(fetchInitialCode()) const html = renderToString( <Provider store={store}> <App /> </Provider> ) return { html, pageContext: { initialState: store.getState() // vite-plugin-ssr自动挂载到window.__INITIAL_STATE__ } } }
关键说明
- 先使用服务端状态完成Hydration,彻底避免客户端初始UI与服务端不匹配的问题
- 延迟恢复本地状态的时机是Hydration完成后,此时DOM已稳定,更新状态只会触发客户端渲染,不会触发Hydration校验
- 建议合并状态而非直接替换整个Store,避免覆盖服务端预取的有效数据
内容的提问来源于stack exchange,提问作者antun
相关产品推荐
相关产品推荐

