reactjsreduxnext.jsnext-redux-wrapper

Next.js with next-redux-wrapper state is being reset to initial value when navigating using Link


Next.js v12.0

next-redux-wrapper.

Whenever I navigate away from the page using the appropriate next/link element and then back again (using another link el) the state is reset to the initial value and so another fetch is executed. What is strange about this is that I have another 'transaction' slice setup in an identical manner except it holds an array of transaction objects and that one is working just fine (navigate away and back and the data is not re-fetched as it persisted in store) code is below any suggestions would be greatly appreciated.

store.js

import { HYDRATE, createWrapper } from "next-redux-wrapper";
import thunkMiddleware from "redux-thunk";
import address from "./address/reducer";
import transactions from "./transaction/reducer";

const bindMiddleware = (middleware) => {
  if (process.env.NODE_ENV !== "production") {
    const { composeWithDevTools } = require("redux-devtools-extension");
    return composeWithDevTools(applyMiddleware(...middleware));
  }
  return applyMiddleware(...middleware);
};

const combinedReducer = combineReducers({
  transactions,
  address,
});

const rootReducer = (state, action) => {
  if (action.type === HYDRATE) {
    const nextState = {
      ...state, // use previous state
      ...action.payload, // apply delta from hydration
    };
    
    if (state.address.id){
      nextState.address = state.address;
    }
    return nextState;
  } else {
    return combinedReducer(state, action);
  }
};


const initStore = () => {
  return createStore(rootReducer, bindMiddleware([thunkMiddleware]));
};
export const wrapper = createWrapper(initStore);

address/reducer.js


const addressInitialState = {
  id: null,
  timestamp: null,
  address: null,
  balance: null,
  received: null,
  sent: null,
  groupid: null,
  last_txs: []
};

export default function reducer(state = addressInitialState, action) {
  switch (action.type) {
    case addressActionTypes.GET_WALLET_DETAILS:
      return {id: action.payload.address, ...action.payload};
    default:
      return state;
  }
}

address/action.js

export const addressActionTypes = {
  GET_WALLET_DETAILS: "GET_WALLET_DETAILS",
};

export const getWalletDetails = (address) => {
  return async (dispatch) => {
    const fetchData = async () => {
      const response = await fetch(
        `https:someapi.com/api/getaddress/?address=${address}`
      );
      if (!response.ok) {
        throw new Error("Could not fetch address data!");
      }

      const data = await response.json();
      console.log('req sent');
      return data;
    };
    try {
      const addressData = await fetchData();
      dispatch({
        type: addressActionTypes.GET_WALLET_DETAILS,
        payload: addressData,
      });
    } catch (err) {
      console.log(err);
    }
  };
};

pages/[address].js

import { Fragment } from "react";
import Head from "next/head";
import AddressDetails from "../../../components/crypto/rvn/AddressDetails";
import AddressTransactions from "../../../components/crypto/rvn/AddressTransactions";
import { connect } from "react-redux";
import { getWalletDetails } from "../../../store/address/action";
import { wrapper } from "../../../store/store";

function Address(props) {
  return (
    <Fragment>
      <Head>
        <title>RVN</title>
        <meta name="description" content="RVN Address" />
      </Head>
      <AddressDetails address={props.addressDetails}></AddressDetails>
      <AddressTransactions
        transactions={props.addressDetails["last_txs"]}
        address={props.addressDetails.address}
      ></AddressTransactions>
    </Fragment>
  );
}

export const getServerSideProps = wrapper.getServerSideProps(
  (store) => async (context) => {
    const state = store.getState();
    if(state.address.id === null) {
      await store.dispatch(getWalletDetails(context.params.address));
    }
    else{
      return{
        props: {
          addressDetails: state.address,
        }
      }
    }
  }
);


const mapStateToProps = (state) => ({
  addressDetails: state.address,
});
export default connect(mapStateToProps, null)(Address);

Solution

  • Solved this by converting this

    const initStore = () => {
      return createStore(rootReducer, bindMiddleware([thunkMiddleware]));
    };
    

    in store.js which direclty from https://github.com/vercel/next.js/tree/canary/examples/with-redux-thunk

    to this

    const store = createStore(rootReducer, bindMiddleware([thunkMiddleware]));
    
    const initStore = () => {
      return store
    };
    

    so it does not reinitialize the store every time the wrapper is used

    this is more in line with the documentation at https://github.com/kirill-konshin/next-redux-wrapper