I am trying to integrate next-redux-wrapper to Next.js RTK project. When invoking async action from getServerSideProps, I am getting state mismatch error (see the image below).
When I dispatch action from client side (increment/decrement), everything works well. I think issue is related to HYDRATION but so far all my efforts have failed.
I tried mapping redux state to props, storing props in component state, added if statements to check values but nothing seem to work. I've been stuck on this for 2 weeks. I'm not sure what else to try next.
store/store.js
import { configureStore, combineReducers } from "@reduxjs/toolkit";
import counterReducer from "./slices/counterSlice";
import { createWrapper, HYDRATE } from "next-redux-wrapper";
const combinedReducer = combineReducers({
counter: counterReducer,
});
const reducer = (state, action) => {
if (action.type === HYDRATE) {
const nextState = {
...state, // use previous state
...action.payload, // apply delta from hydration
};
return nextState;
} else {
return combinedReducer(state, action);
}
};
export const makeStore = () =>
configureStore({
reducer,
});
export const wrapper = createWrapper(makeStore, { debug: true });
store/slices/counterSlice.js
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
const initialState = {
value: 0,
data: { quote: "" },
pending: false,
error: false,
};
export const getKanyeQuote = createAsyncThunk(
"counter/kanyeQuote",
async () => {
const respons = await axios.get("https://api.kanye.rest/");
return respons.data;
}
);
export const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
extraReducers: (builder) => {
builder
.addCase(getKanyeQuote.pending, (state) => {
state.pending = true;
})
.addCase(getKanyeQuote.fulfilled, (state, { payload }) => {
state.pending = false;
state.data = payload;
})
.addCase(getKanyeQuote.rejected, (state) => {
state.pending = false;
state.error = true;
});
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
pages/index.js
import React, { useState } from "react";
import { useSelector, useDispatch, connect } from "react-redux";
import {
decrement,
increment,
getKanyeQuote,
} from "../store/slices/counterSlice";
import { wrapper } from "../store/store";
function Home({ data }) {
const count = useSelector((state) => state.counter.value);
// const { data, pending, error } = useSelector((state) => state.counter);
const dispatch = useDispatch();
const [quote, setQuote] = useState(data.quote);
return (
<div style={{ display: "flex", flexDirection: "column" }}>
{/* <span>{pending && <p>Loading...</p>}</span>
<span>{error && <p>Oops, something went wrong</p>}</span> */}
<div>{quote}</div>
<span>Count: {count}</span>
<div>
<button
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
Increment
</button>
<button
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
Decrement
</button>
</div>
</div>
);
}
export const getServerSideProps = wrapper.getServerSideProps(
(store) =>
async ({ req, res, ...etc }) => {
console.log(
"2. Page.getServerSideProps uses the store to dispatch things"
);
await store.dispatch(getKanyeQuote());
}
);
function mapStateToProps(state) {
return {
data: state.counter.data,
};
}
export default connect(mapStateToProps)(Home);
This might stem from a known issue where next-redux-wrapper 8 hydrates too late. Please try downgrading to version 7 for now and see if that resolves the problem.