question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Persist redux value not working with next-redux-wrapper

See original GitHub issue

Describe the bug

I am useing redux-toolkit, redux-persist, next-redux-wrapper. When saving data to the storage space, it works normally. But refresh page, the data not persisted…

To Reproduce

package.json

{
  "name": "jebs-next",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "NODE_ENV='development' node server.js",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "format": "prettier --write \"src/**/*\"",
    "local": "nodemon ./server.js localhost 3080"
  },
  "dependencies": {
    "@hookform/resolvers": "^2.8.2",
    "@reduxjs/toolkit": "^1.6.2",
    "@types/parse-link-header": "^1.0.0",
    "@types/react-redux": "^7.1.19",
    "axios": "^0.22.0",
    "babel-plugin-styled-components": "^1.13.2",
    "browser-image-compression": "^1.0.16",
    "express": "^4.17.1",
    "google-map-react": "^2.1.10",
    "next": "11.1.2",
    "next-i18next": "^8.9.0",
    "next-redux-wrapper": "^7.0.5",
    "nodemon": "^2.0.4",
    "parse-link-header": "^1.0.1",
    "random-id": "^1.0.4",
    "react": "17.0.2",
    "react-dom": "17.0.2",
    "react-hook-form": "^7.17.2",
    "react-hotkeys-hook": "^3.4.3",
    "react-phone-input-2": "^2.14.0",
    "react-redux": "^7.2.5",
    "react-toastify": "^8.0.3",
    "redux-persist": "^6.0.0",
    "styled-components": "^5.3.1",
    "styled-system": "^5.1.5",
    "typescript": "4.4.3",
    "yup": "^0.32.11"
  },
  "devDependencies": {
    "@types/browser-image-compression": "^1.0.9",
    "@types/google-map-react": "^2.1.3",
    "@types/react": "17.0.27",
    "@types/styled-components": "^5.1.14",
    "@types/styled-system": "^5.1.13",
    "@typescript-eslint/eslint-plugin": "^4.33.0",
    "@typescript-eslint/parser": "^4.33.0",
    "babel-eslint": "^10.1.0",
    "eslint": "7.32.0",
    "eslint-config-airbnb": "^18.2.1",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-babel": "^5.3.1",
    "eslint-plugin-import": "^2.25.2",
    "eslint-plugin-jsx-a11y": "^6.4.1",
    "eslint-plugin-prettier": "^4.0.0",
    "eslint-plugin-react": "^7.26.1",
    "eslint-plugin-react-hooks": "^4.2.0",
    "http-proxy-middleware": "^2.0.1",
    "prettier": "^2.4.1"
  }
}

rootReducer.ts

import { CombinedState, combineReducers } from '@reduxjs/toolkit'
import { HYDRATE } from 'next-redux-wrapper'
import { persistReducer } from 'redux-persist'
import storageSession from 'redux-persist/lib/storage/session'
import { KEY_SLICE_ROOT, KEY_SLICE_STORAGE } from '../../common/key'
import authenticationReducer, { AuthState } from './authSlice'
import chatsReducer, { ChatsState } from './chatsSlice'
import progressReducer, { ProgressState } from './progressSlice'
import storageReducer, { StorageState } from './storageSlice'
import userReducer, { UserState } from './userSlice'

const persistConfig = {
  key: KEY_SLICE_ROOT,
  storage: storageSession,
  whitelist: [KEY_SLICE_STORAGE],
}

const rootReducer = (
  state: any,
  action: any,
): CombinedState<{
  progress: ProgressState
  authentication: AuthState
  chats: ChatsState
  storage: StorageState
  user: UserState
}> => {
  if (action.type === HYDRATE) {
    return {
      ...state,
      ...action.payload,
    }
  }
  return combineReducers({
    progress: progressReducer,
    authentication: authenticationReducer,
    chats: chatsReducer,
    storage: storageReducer,
    user: userReducer,
  })(state, action)
}

export type RootState = ReturnType<typeof rootReducer>

export default persistReducer(persistConfig, rootReducer)

store.ts

import { Action, configureStore } from '@reduxjs/toolkit'
import { createWrapper } from 'next-redux-wrapper'
import { persistStore } from 'redux-persist'
import { ThunkAction } from 'redux-thunk'
import reducer, { RootState } from './features'

const store = configureStore({
  reducer,
  devTools: process.env.NODE_ENV !== 'production',
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: false,
    }),
})

const makeStore = () => store

export type AppDispatch = typeof store.dispatch
export type AppThunk = ThunkAction<void, RootState, unknown, Action<string>>

export const persistor = persistStore(store) // Nasty hack
// next-redux-wrapper에서 제공하는 createWrapper정의
export const wrapper = createWrapper(makeStore, {
  debug: process.env.NODE_ENV !== 'production',
})

export default store

_app.tsx

import { NextPage } from 'next'
import { appWithTranslation } from 'next-i18next'
import type { AppProps } from 'next/app'
import Head from 'next/head'
import 'react-phone-input-2/lib/bootstrap.css'
import { Provider } from 'react-redux'
import 'react-toastify/dist/ReactToastify.css'
import { PersistGate } from 'redux-persist/integration/react'
import { ThemeProvider } from 'styled-components'
import ProgressBar from '../components/ProgressBar'
import Spinner from '../components/Spinner'
import Toast from '../components/Toast'
import store, { persistor, wrapper } from '../store'
import '../styles/globals.css'

const App: NextPage<AppProps> = ({ Component, pageProps }) => {
  return (
    <>
      <Head>
        <title>jebs</title>
      </Head>
      <Provider store={store}>
        <PersistGate persistor={persistor} loading={<Spinner />}>
          <ThemeProvider
            theme={{
              breakpoints: ['501px', '769px', '1920px'],
            }}
          >
            <ProgressBar />
            <Toast />
            <Component {...pageProps} />
          </ThemeProvider>
        </PersistGate>
      </Provider>
    </>
  )
}

export default wrapper.withRedux(appWithTranslation(App))

storageSlice.ts

import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { KEY_SLICE_STORAGE } from '../../common/key'

export interface StorageState {
  accessToken: string | null
}

const initialState: StorageState = {
  accessToken: null,
}

const storage = createSlice({
  name: KEY_SLICE_STORAGE,
  initialState,
  reducers: {
    saveAccessToken(state, action: PayloadAction<string>) {
      state.accessToken = action.payload
    },
  },
})

export const { saveAccessToken } = storage.actions

export default storage.reducer

userSlice.ts

...

export const fetchConfirmPhoneCodeAndValues =
  (uid: string, name: string, phone: string, code: string): AppThunk =>
  async (dispatch) => {
    try {
      dispatch(loading())

      const { statusCode, message } = await getCheckFindPWPhoneAndValues(
        uid,
        name,
        phone,
        code,
      )
      if (statusCode && statusCode === 200) {
        toast.success(MESSAGE_CONFIRM_CODE_SUCCESS)

        dispatch(confirmCodeSuccess())
        dispatch(
          saveAccessToken(
            'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IlZ0Mm1NSXVqYkFoT0dHOC0iLCJpYXQiOjE2MzU5MTQ2E',
          ),
        )
      } else {
        throw new Error(message ?? '')
      }
    } catch (err) {
      if (err) {
        const { message } = err as ErrorState
        toast.error(message)
      } else {
        toast.error(ERROR_BASIC_MESSAGE)
      }
    }
  }

...

Expected behavior

Even if the page is switched or refreshed, the value of accsses_token in storage must be maintained.

Screenshots

Before refresh or switch page

image

After refresh or switch page

image

Desktop (please complete the following information):

  • OS: MacOS version 11.4
  • Browser : chrome

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:1
  • Comments:6

github_iconTop GitHub Comments

2reactions
simplenotezycommented, Nov 30, 2021

Have a look at https://github.com/fazlulkarimweb/with-next-redux-wrapper-redux-persist

I was hoping that repo had the solution, as it seemed promising. Unfortunately it doesn’t work, as the screen will still be white if javascript is disabled.

Also created an issue here: https://github.com/vercel/next.js/discussions/31884

It’s amazing how such a single feature turns out to be so challenging. Oh well, hence my username.

2reactions
masterambicommented, Nov 4, 2021

i got the same problem

Read more comments on GitHub >

github_iconTop Results From Across the Web

next.js - I have successfully implemented the redux-persist ...
You have to put inside that string array the redux reducers values you want to be persisted. Example: const persistConfig = { key:...
Read more >
Cant use next-redux-wrapper with redux : r/nextjs - Reddit
the error I am facing : I can't use the ReturnType for the store to set up the next redux wrapper, any help?...
Read more >
How to use Redux in Next.js - LogRocket Blog
Learn how to incorporate Redux, the most popular state management tool, into Next.js quickly and easily in this full tutorial.
Read more >
hitorisensei/next-redux-wrapper v6.0.0 - npm.io
import React, {Component} from 'react'; import {NextPage} from 'next'; import {wrapper, State} from '../store'; // you can also use `connect()` instead of hooks ......
Read more >
How to setup store and Redux Persist with Next.js for SSR
If you want to use Redux Persist with Next.js with SSR constraints, you have to follow some logic to make your code work...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found