I am doing a NextJS project using next-redux-wrapper, redux/toolkit and redux-saga. This is my setup:
store/enquiry/slice.ts:
import { PayloadAction, createSlice } from '@reduxjs/toolkit'
import { HYDRATE } from 'next-redux-wrapper';
export enum LoadingTracker {
NotStarted,
Loading,
Success,
Failed
}
type EnquiryState = {
enquiries: Enquiry[],
loadingTracker: LoadingTracker
}
const initialState: EnquiryState = {
enquiries: [],
loadingTracker: LoadingTracker.NotStarted
}
export const enquirySlice = createSlice({
name: 'enquiries',
initialState,
reducers: {
//@ts-ignore
loadEnquiries: (state, action: PayloadAction<Enquiry[]>) => {
state.enquiries = action.payload;
state.loadingTracker = LoadingTracker.Success
},
failedLoadEnquiries: (state) => {
state.loadingTracker = LoadingTracker.Failed;
},
startedLoadingEnquiries: (state) => {
state.loadingTracker = LoadingTracker.Loading;
}
},
extraReducers:{
[HYDRATE]:(state, action) => {
return{
...state,
...action.payload.enquiry
};
}
},
});
export const { loadEnquiries, failedLoadEnquiries, startedLoadingEnquiries } =
enquirySlice.actions;
export default enquirySlice.reducer;
This is my enquiry saga setup: sagas/enquiry/saga.ts
import { call, put, takeLatest } from "redux-saga/effects";
import { loadEnquiries, startedLoadingEnquiries, failedLoadEnquiries } from
"../../store/enquiry/slice";
export function* loadEnquiriesSaga() {
try {
//@ts-ignore
let response = yield call(() =>
fetch("https://xxxxx686888wwg326et2.mockapi.io/enqueries")
);
if (response.ok) {
//@ts-ignore
let result = yield call(() => response.json());
yield put(loadEnquiries(result));
} else {
yield put(failedLoadEnquiries());
}
} catch (e) {
yield put(failedLoadEnquiries());
}
}
export function* loadEnquiriesWatcher() {
const { type } = startedLoadingEnquiries();
yield takeLatest(type, loadEnquiriesSaga);
}
This is my root saga setup: sagas/index.js
import { all } from 'redux-saga/effects';
import { loadEnquiriesWatcher } from './enquiry/saga';
export default function* rootSaga() {
yield all([
loadEnquiriesWatcher()
])
}
this is my store setup: store/index.js
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import createSagaMiddleware from "redux-saga";
import enquiryReducer from '../store/enquiry/slice';
import { createWrapper } from 'next-redux-wrapper';
import rootSaga from '../sagas/index';
const makeStore = () => {
const sagaMiddleware = createSagaMiddleware();
const store = configureStore({
reducer: {
enquiry: enquiryReducer,
},
middleware: [...getDefaultMiddleware({ thunk: false }), sagaMiddleware],
devTools: true
})
//@ts-ignore
store.sagaTask = sagaMiddleware.run(rootSaga);
return store;
}
const store = makeStore();
export type AppStore = ReturnType<typeof makeStore>;
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
export const useAppDispatch: () => AppDispatch = useDispatch
This is _app.ts file
import React, { useEffect } from "react";
import '../styles/globals.css'
import type { AppProps } from 'next/app'
import { QueryClient, QueryClientProvider } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import { Provider } from 'react-redux'
import { wrapper } from '../store/index';
const App = ({ Component, ...rest }: AppProps) => {
const [queryClient] = React.useState(() => new QueryClient());
const { store, props } = wrapper.useWrappedStore(rest);
const { pageProps } = props;
return (
<Provider store={store}>
<QueryClientProvider client={queryClient}>
<Component {...pageProps} />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
</Provider>
);
}
export default App;
I am running a getServersideProps function in pages/index.js file.
import Head from 'next/head'
import { Inter } from '@next/font/google'
import utilStyles from '../styles/Utils.module.css';
import Layout from '../components/layout';
import { wrapper } from '../store';
import { startUserLogin } from '../store/user/slice';
import { END } from 'redux-saga';
import { GetServerSideProps } from 'next';
import { startedLoadingEnquiries } from '../store/enquiry/slice';
const inter = Inter({ subsets: ['latin'] })
export const getServerSideProps: GetServerSideProps = wrapper.getServerSideProps(
//@ts-ignore
(store) => async ({ req }) => {
await store.dispatch(startedLoadingEnquiries());
store.dispatch(END);
await (store as any).sagaTask.toPromise();
return {
props:{
status:1
}
}
})
export default function Home(props:any) {
return (
<Layout home>
<Head>
<title>Holiday Experts</title>
</Head>
<section className={utilStyles.headingMd}>
<p>Introduction to Holiday Exers blah blah blah............. status:
{props.status}</p>
</section>
<section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
<h2 className={utilStyles.headingLg}>To Do: List of Top Destinatons as
cards</h2>
</section>
</Layout>
)
}
When I got to the home page localhost:3000/, I see all the state values are populated. I go to localhost:3000/enquiries and i see a list of enquiries as well, but when I refresh, the list is empty because, client side state is not set. If I open Redux dev tools, I see enquiry state to be empty all the time.
I think there is something missing during the Hydration of the state. Not sure what it is.Can somebody help me with it?
Here are my references that I followed: