You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Remix + Redux SSR场景下如何同步预渲染页面的初始状态?

Remix + Redux SSR 初始状态同步实现方案

1. 创建支持预加载状态的Redux Store

先编写可接收预加载状态的Store创建函数,确保服务端和客户端能基于同一初始状态生成Store:

// app/store.ts
import { configureStore } from '@reduxjs/toolkit';
import rootReducer from './reducers';

// 接收预加载状态作为参数
export function makeStore(preloadedState = {}) {
  return configureStore({
    reducer: rootReducer,
    preloadedState,
    // 生产环境关闭devTools
    devTools: process.env.NODE_ENV !== 'production',
  });
}

// 导出类型用于类型提示
export type AppStore = ReturnType<typeof makeStore>;
export type RootState = ReturnType<AppStore['getState']>;
export type AppDispatch = AppStore['dispatch'];

2. 在根组件Loader中获取全局初始状态

利用Remix服务端Loader获取全局通用数据(如用户信息、系统配置),将其作为初始状态传递给客户端:

// app/root.tsx
import { Outlet, useLoaderData } from '@remix-run/react';
import { Provider } from 'react-redux';
import { makeStore } from './store';

// 服务端执行:获取全局初始数据
export async function loader() {
  // 示例:从数据库/API拉取用户信息和系统设置
  const user = await fetchCurrentUser();
  const appSettings = await fetchAppSettings();

  // 返回可序列化的初始状态对象
  return {
    user: { data: user },
    settings: appSettings,
  };
}

export default function App() {
  // 获取Loader返回的初始状态(服务端、客户端均可获取)
  const initialState = useLoaderData<typeof loader>();
  // 基于初始状态创建Store
  const store = makeStore(initialState);

  return (
    <html lang="zh-CN">
      <body>
        <Provider store={store}>
          <Outlet />
        </Provider>
      </body>
    </html>
  );
}

3. 页面级数据同步到Redux

针对页面专属数据,在对应页面的Loader中获取数据,再在组件内同步到Redux Store:

// app/routes/posts.tsx
import { useLoaderData, useDispatch } from '@remix-run/react';
import { setPosts } from '../features/posts/postsSlice';
import { RootState } from '../store';
import { useSelector } from 'react-redux';

// 服务端执行:获取页面专属数据
export async function loader() {
  const posts = await fetchPostsList();
  return { posts };
}

export default function PostsPage() {
  const { posts } = useLoaderData<typeof loader>();
  const dispatch = useDispatch();
  // 从Redux获取数据,确保渲染基于Store状态
  const reduxPosts = useSelector((state: RootState) => state.posts.list);

  // 同步数据到Redux:服务端渲染时执行,客户端hydrate时也会执行
  // 通过比对避免重复dispatch,确保两端状态一致
  if (JSON.stringify(reduxPosts) !== JSON.stringify(posts)) {
    dispatch(setPosts(posts));
  }

  return (
    <div className="posts-list">
      {reduxPosts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.content}</p>
        </article>
      ))}
    </div>
  );
}

关键注意事项

  • 序列化要求:Loader返回的初始状态必须是可序列化的(不能包含函数、Symbol、循环引用等),否则Remix无法在服务端与客户端间传递数据。
  • Hydration一致性:客户端创建Store时必须使用与服务端完全相同的初始状态,否则会出现Hydration错误,通过useLoaderData可确保两端数据一致。
  • 服务端Store隔离:每个服务端请求都会创建独立的Store实例,无需担心状态污染问题。

内容的提问来源于stack exchange,提问作者Meow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 06:35:30