Search code examples
next.jsreduxreact-reduxserver-side-renderingredux-toolkit

How to initialize store from ssr data?


I'm working on a Next.js project organized into directories, and I'm also implementing Redux for client-side state management. I have pages located in the app directory t.

Additionally, I'm fetching some data on the server side and want to initialize my Redux store with this server-side rendered data.

Here's an example of how I'm fetching data in my Home page component:

Home.tsx

import { Box, Button, Typography } from "@mui/material";
import blogsecure from "./services/blogSecure";
import { BlogGetData } from "./models/BlogGet";

export const fetchData = async () => {
  const response = await blogsecure.get('/blogs/fetch_all'); //<- I want to initalize from this data

  if (response.status !== 200) {
    throw new Error("Failed to fetch API data");
  }
  return response.data;
};

const Home = async() => {
  const blogs: BlogGetData[] = await fetchData();
  return (
    <Box sx={{ position: 'relative', height: '100%' }}>
      <Button sx={{ position: 'absolute', bottom: 16, right: 16 }}>
        Create blog
      </Button>
      {blogs.map((blog) => <Typography>{blog.title}</Typography>)}
    </Box>
  );
}

export default Home;

And here's how I'm handling Redux initialization in my StoreProvider.tsx and store.ts files:

StoreProvider.tsx

import { useRef } from 'react';
import { Provider } from 'react-redux';
import { AppStore, makeStore } from '../lib/store';

const StoreProvider = ({ children }: { children: React.ReactNode }) => {
  const storeRef = useRef<AppStore>();
  if (!storeRef.current) {
    storeRef.current = makeStore();
  }

  return <Provider store={storeRef.current}>{children}</Provider>;
};

export default StoreProvider;

store.ts

import { configureStore } from '@reduxjs/toolkit';
import authReducer from './feature/auth/authSlice';
import blogsReducer from './feature/blogs/blogFetchSlice';
import userReducer from './feature/user/userSlice';

export const makeStore = () => {
  return configureStore({
    reducer: {
      auth: authReducer,
      user: userReducer,
      blogs: blogsReducer,
    }
  });
}

export type AppStore = ReturnType<typeof makeStore>;
export type RootState = ReturnType<AppStore['getState']>;
export type AppDispatch = AppStore['dispatch'];

In my Redux slice for fetching blogs (blogFetchSlice.tsx), I define initial state and reducers as follows:

blogFetchSlice.tsx

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import { BlogFetchAllState } from './types'; // Assuming you have defined types for state

export const fetchAllBlogs = createAsyncThunk(
  // my async logic here
);

const initialState: BlogFetchAllState = {
  blogs: null,<-- If I initialize here it is done from client side
  loading: false,
  error: null,
  current_blog: null
};

const blogFetchSlice = createSlice({
  name: 'blogFetchAll',
  initialState,
  reducers: {
    // Additional reducers if needed
  },
  extraReducers: (builder) => {
    // slice actions
  }
});

export default blogFetchSlice.reducer;

This setup initializes the Redux store with the server-side rendered data and provides a structure for managing your client-side state with Redux. If you need further assistance or clarification, feel free to ask!

I'm structuring a Next.js project with Redux for client-side state management. Each page resides in the app directory for better organization. I need guidance on initializing the Redux store with data fetched on the server side. Can you help me set up Redux initialization from server-side rendered data in this Next.js project?

Solution

  • This is currently not possible, as your Next.js App Router application runs in the server and in the browser at the same time - so your store is already created in the browser before the server is finished rendering.

    React will need to add some primitives to inject data into the stream before this is possible with Redux.