bjoluc / next-redux-cookie-wrapper

Sync a subset of your Redux state with cookies in Next.js :cookie: :sparkles:
MIT License
114 stars 4 forks source link

State from Cookies not set back after browser reload #30

Closed mhmdbhsk closed 2 years ago

mhmdbhsk commented 2 years ago

Hi sir @bjoluc. I hope you are well,

Sorry in advance. I have a problem, i.e. my app.theme state is not set from the state already stored in cookies. but returns to initial value when browser is reloaded. I don't know if there is an error in my next-redux-wrapper or next-redux-cookie-wrapper code. I attach a video recording and some source code of the problem I found

Stack

https://user-images.githubusercontent.com/34903088/152652063-4e573ced-bc74-49dc-aa22-0988a4b57f8a.mp4

AppReducers

import { AppActionType } from 'global/action-types';
import { HYDRATE } from 'next-redux-wrapper';
import { AnyAction } from 'redux';

export enum ThemeAppEnum {
  LIGHT = 'LIGHT',
  DARK = 'DARK',
}

interface State {
  theme: ThemeAppEnum;
  locale: string | null;
  sidebar: boolean;
  server?: any;
  client?: any;
}

const initialState: State = {
  theme: ThemeAppEnum.LIGHT,
  locale: null,
  sidebar: true,
};

const reducer = (state: State = initialState, action: AnyAction) => {
  switch (action.type) {
    case HYDRATE:
      return {
        ...state,
        server: {
          ...state.server,
          ...action.payload.server,
        },
      };
    case 'SERVER_ACTION':
      return {
        ...state,
        server: {
          ...state.server,
        },
      };
    case 'CLIENT_ACTION':
      return {
        ...state,
        client: {
          ...state.client,
        },
      };
    case AppActionType.UPDATE_LOCALE_APP:
      return {
        ...state,
        theme: action.payload,
      };
    case AppActionType.UPDATE_SIDEBAR_COLLAPSED_APP:
      return {
        ...state,
        sidebar: action.payload,
      };
    case AppActionType.UPDATE_THEME_APP:
      return {
        ...state,
        theme: action.payload,
      };
    default:
      return state;
  }
};

export default reducer;

Store

import { createStore, applyMiddleware } from 'redux';
import { createWrapper } from 'next-redux-wrapper';
import {
  nextReduxCookieMiddleware,
  wrapMakeStore,
} from 'next-redux-cookie-wrapper';
import { createLogger } from 'redux-logger';
import { composeWithDevTools } from 'redux-devtools-extension';
import reducer from './reducers';
import thunkMiddleware from 'redux-thunk';

const bindMiddleware = (middleware: any) => {
  if (process.env.NODE_ENV !== 'production') {
    return composeWithDevTools(applyMiddleware(...middleware));
  }

  return applyMiddleware(...middleware);
};

const logger = createLogger({ diff: true });

const makeStore = wrapMakeStore(() =>
  createStore(
    reducer,
    bindMiddleware([
      nextReduxCookieMiddleware({
        subtrees: [
          {
            subtree: 'app.theme',
            compress: false,
          },
        ],
      }),
      logger,
      thunkMiddleware,
    ])
  )
);

export const wrapper = createWrapper(makeStore, { debug: true });

Sorry if my question is silly, but I'm really confused where is the wrong part. Thank you in advance.

bjoluc commented 2 years ago

Hi @mhmdbhsk, thanks, I'm fine :)

I think the problem here is in the HYDRATE reducer: When you reload the page, it is called with a HYDRATE action which is supposed to merge incoming state (including the cookie state) with the current state. On page reloads with SSR the "current" state is the initial state and the incoming state is the cookie state, as far as available. You are only using action.payload.server in the action handler and discarding action.payload.app, so the cookie state is ignored. Also, when you implement the reducer, note that action.payload.app may also be undefined if no cookie is set. Let me know if there's anything I should clarify, or if this doesn't solve the issue.

Cheers!

mhmdbhsk commented 2 years ago

Ahhh, I see.

It's working now. Thank you very much!