localStorage is not defined in Nextjs, Redux and Typescript

Viewed 63

I'm facing a problem with my project. The problem is ReferenceError: localStorage is not defined. I'm using Nextjs, and Redux with Typescript.

Screenshot_2

const storedUser: string | null = localStorage.getItem('user');
const user: DisplayUser | null = !!storedUser ? JSON.parse(storedUser) : null;

const storedJwt: string | null = localStorage.getItem('jwt');
const jwt: Jwt = !!storedJwt ? JSON.parse(storedJwt) : null;

I'm using these 2 variables user and jwt in here initialState

const initialState: AuthState = {
    user: user,
    jwt: jwt,
    isLoading: false,
    isSuccess: false,
    isError: false,
}

And initialState are used in authSlice function

export const authSlice = createSlice({
  name: 'auth',
    initialState,
    reducers: {
        reset: (state) => {
            state.isLoading = false;
            state.isSuccess = false;
            state.isError = false;
        }
    },
})

3 Answers

If you are using React.js or Next.js and you want to check :

  • if you are on the Browser (which means you can use the localStorage)
  • or if you are on the Server (which means you cannot use localStorage)

You need to check if the window variable is not undefined, example :

if (typeof window !== 'undefined') {
  console.log('You are on the browser')
  // ️ can use localStorage here
} else {
  console.log('You are on the server')
  // ️ can't use localStorage
}

similar discussion on github/vercel/next.js/discussions

Adding onto Omar's answer - if you need to store something from localStorage in a Redux store, you'd need to grab that information client-side and then pass it into your initial Redux store. Redux itself will not be able to differentiate between client/server side rendering unless you check typeof window !== 'undefined'.

If you want to use Omar's answer as-is, you would need to make that calculation inside your Redux reducer when the initial state is being calculated. I don't recommend bringing that logic inside your reducer (makes it harder to unit test the reducer) - try calculating that logic outside of the initial Redux store, then pass it in (minimal example below)

// However you calculate your initial Redux state should replace INITIAL_REDUX_STATE
const [statefulStore, setStatefulStore] = useState(INITIAL_REDUX_STATE);
const reduxStore = createStore(yourReducer, initialStore);
useEffect(() => {
  // Once and only once on the initial render, grab the localStorage value
  // and update Redux state
  setStatefulStore(oldState => {
    return Object.assign(oldState, {
      ssrValue: localStorage.getItem('user')
    });
  });
}, []);

You cannot run localStorage in Node because it's a browser feature. You can run a util and reuse it like this:

const getFromLocalStorage = (key) => {
    if (!key || typeof window === 'undefined') {
        return ""
    }
    return localStorage.getItem('user')
}

You can import this in other files and use it:

const userId = getFromLocalStorage('userId');

This is the simplest way of using this.

Related