Toni Suwendi
Toni Suwendi

Reputation: 401

In React and Next.js constructor, I am getting "Reference Error: localstorage is not defined"

I make a system jsonwebtoken in React and use Next.js. I find a problem when I run the code in the browser, that is, "localStorage is not defined". How can I fix it?

This is my code in file AuthStudentContext.js:

import React from 'react'
import axios from 'axios'

const axiosReq = axios.create()
const AuthStudentContext = React.createContext()

export class AuthStudentContextProvider extends React.Component {

    constructor() {
        super()
        this.state = {
            students: [],
            student: localStorage.getItem('student') || {},
            token: localStorage.getItem('token') || "",
            isLoggedIn: (localStorage.getItem('student' == null)) ? false : true
        }
    }

    login = (credentials) => {
        return axiosReq.post("http://localhost:4000/api/login", credentials)
            .then(response => {
                const { token } = response.data
                localStorage.setItem("token", token)

                this.setState({
                    token,
                    isLoggedIn: true
                })

                return console.log(response)
            })
    }

And it shows error "localStorage is not defined".

Upvotes: 34

Views: 62722

Answers (8)

Alkan Polat
Alkan Polat

Reputation: 9

this is working

export const Theme = { Light: "light", Dark: "dark" };

const [currentTheme, setCurrentTheme] = useState<string>();

  // Local storage
  useEffect(() => {
    if (typeof window !== "undefined" && currentTheme) {
      localStorage.setItem("theme", currentTheme);
    }
  }, [currentTheme]);

  useEffect(() => {
    setCurrentTheme(localStorage.getItem("theme") || Theme.Light);
  }, []);

  const siwtchTheme = () => {
    currentTheme === Theme.Light
      ? setCurrentTheme(Theme.Dark)
      : setCurrentTheme(Theme.Light);
  };

Upvotes: 1

ashuvssut
ashuvssut

Reputation: 2305

localStorage is only available client side. So Next.js will throw an error if you attempt to access localStorage to getItem or setItem.

Instead, inside the component, use a useEffect hook to access the localStorage on first client side render.

useEffect(() => {
  localStorage.setItem("abc", "def");
}, []);

Upvotes: 1

Richard Torcato
Richard Torcato

Reputation: 2770

The window object and Localstorage won't be available when Next.js is building. So you need to check if the code is running in the browser. If you are running in React hooks you don't need to do this because hooks are always running browser side in React.

Just add these two utility functions to your Next.js project.

export const isBrowser = (): boolean => {
  return typeof window !== 'undefined'
}

export const nextLocalStorage = (): Storage | void => {
  if (isBrowser()) {
    return window.localStorage
  }
}

Then you can use it in your code like this:

nextLocalStorage()?.setItem('user', JSON.stringify(user))

Upvotes: 3

Smart Samuel
Smart Samuel

Reputation: 51

In addition to what SILENT said, this works for me:

React.useEffect(() => {
    if (localStorage) {
        const getLocalState = localStorage.getItem("headless");
        console.log("LocalState: ", getLocalState)
    }
}, []);

Upvotes: 5

SILENT
SILENT

Reputation: 4278

As everyone already mentioned, Next.js runs both on the client and server. On the server, there isn't any localStorage, hence the undefined error.

However, an alternative solution is to check if Next.js is running on the server before accessing the localStorage. I.e.,

const ISSERVER = typeof window === "undefined";

if(!ISSERVER) {
    // Access localStorage
    ...localStorage.get...
}

Upvotes: 32

wentjun
wentjun

Reputation: 42596

In the constructor, as well as componentWillMount lifecycle hooks, the server is still rendering the component. On the other hand, localStorage exists as part of the browser's window global, and thus you can only use it when the component is rendered. Therefore you can only access localStorage in the componentDidMount lifecycle hook. Instead of calling localStorage in the constructor, you can define an empty state, and update the state in componentDidMount when you can start to call localStorage.

constructor() {
  super()
  this.state = {
    students: [],
    student: undefined
    token: undefined,
    isLoggedIn: undefined
  };
}

componentDidMount() {
  this.login();
  this.setState({
    student: localStorage.getItem('student') || {},
    token: localStorage.getItem('token') || "",
    isLoggedIn: (localStorage.getItem('student' == null)) ? false : true
  });
}

Upvotes: 15

Ilijanovic
Ilijanovic

Reputation: 14924

I never touched Next.js, but I guess its equivalent to Nuxt.js. So it does server-side rendering while you try to access localstorage on the client side.

You will need to use componentDidMount() for this. Here is an example:

componentDidMount(){
   localStorage.setItem('myCat', 'Tom');
   alert("Tom is in the localStorage");
}

Otherwise, you could try with process.browser:

if (process.browser) {
   localStorage.setItem("token", token);
}

Upvotes: 12

Azad Ansari
Azad Ansari

Reputation: 148

I have created a function getLocalStorageItem and called this in useEffect with the required key name. After getting the value from localStorage, saved it in a state(i.e currentUser) and used it in initialState.

 const [currentUser, setCurrentUser] = useState({});

  const getLocalStorageItem = (key) => {
    return typeof window !== undefined
      ? window.localStorage.getItem(key)
      : null;
  };

  useEffect(() => {
    setCurrentUser({
      token: getLocalStorageItem("token"),
      refreshToken: getLocalStorageItem("refreshToken"),
    });
  }, []);

  const initialState = {
    auth: {
      isLoggedIn: true,
      currentUser: currentUser,
    },
  };

Upvotes: 0

Related Questions