5

I'm currently working on my first member area with next.js. For now I want to store some data in localStorage (token, expiresAr, userInfo) - btw, later this is going to be stored in http-only cookie.

With the following code I get the error: "LocalStorage is not defined":

const AuthProvider = ({ children }) => {
   
   const token = localStorage.getItem("token");
   const userInfo = localStorage.getItem("userInfo");
   const expiresAt = localStorage.getItem("expiresAt");
   

  const [authState, setAuthState] = useState({
    token,
    expiresAt,
    userInfo: userInfo ? JSON.parse(userInfo) : {},
  });

  const setAuthInfo = ({ token, userInfo, expiresAt }) => {
    localStorage.setItem("token", token);
    localStorage.setItem("userInfo", JSON.stringify(userInfo));
    localStorage.setItem("expiresAt", expiresAt);

    setAuthState({
      token,
      userInfo,
      expiresAt,
    });
  };

I already tried to use the following snipped:

if (typeof window !== 'undefined') {
const token = localStorage.getItem("token");
const userInfo = localStorage.getItem("userInfo");
const expiresAt = localStorage.getItem("expiresAt");}

But with this code I get the error "token is undefined". So I tried to define the variables const token, const userInfo and const expiresAt globally. But then I get the error: "Unexpected token o in JSON at position 1".

I'm a little stuck with this problem, so any help is appreciated! Thanks!

6
  • 2
    try to console.log(token) in setAuthInfo function, also go to devtools->application->storage->local-storage and check if anything is getting set over there. You might also net JSON.stringify the token inside the setAuthInfo if its value not string Commented Dec 28, 2020 at 15:09
  • Thanks for your help! When go to devtools > aooplication > localStorage I can see the key-values of expiresAt, token and userInfo. Everything looks right to me. So the data are set in localStorage, but I am somehow not able to get them. Any ideas what I could try?
    – Ewax_Du
    Commented Dec 28, 2020 at 17:19
  • Are you sure that you are not trying to get this data on the backend (SSR)? What happens when you console.log(localStorage.getItem("userInfo")), if you data is set properly it should be available directly on localStorage object like so, localStorage.token, if you console.log(localStorage) do you see the property that you set? Commented Dec 28, 2020 at 18:09
  • When I console.log(localStorage.getItem("userInfo")) I get the object with userInfo: something like this {"id": 17, "firstName": "e"} - this object I also see in localStorage and it matches my DB. When I console.log(localStorage) I get a longer string. Something like this: Storage {userInfo: "{"id":17,"firstName":"e"}", expiresAt: "1609183115", ally-supports-cache: "{"userAgent": ...}", token: "eyJhbGciO..", length: 4}. I'm really new in development - so yes, maybe there is a problem with SSR and CSR.
    – Ewax_Du
    Commented Dec 28, 2020 at 18:34
  • I found also found this - maybe this could be helpful: stackoverflow.com/questions/59540321/…
    – Ewax_Du
    Commented Dec 28, 2020 at 18:36

5 Answers 5

3

After playing a little bit more with the code and with the help of you guys, I found the solution:

const AuthProvider = ({ children }) => {
  let token = "";
  let userInfo = "";
  let expiresAt = "";

  if (typeof window !== "undefined") {
    token = localStorage.getItem("token");
    userInfo = localStorage.getItem("userInfo");
    expiresAt = localStorage.getItem("expiresAt");
  }

...

The if-statement tells to run the code, when the window is available. I also had to define the variables (let token, let expiresAt and let userInfo) outside of the if-statement in order to be able to access them in other parts of the code.

Maybe this is helpful for somebody..

0
2

This snippet should work

if (typeof window !== 'undefined') {
  const token = localStorage.getItem("token");
  const userInfo = localStorage.getItem("userInfo");
  const expiresAt = localStorage.getItem("expiresAt");
}

The reason you're getting the error token is undefined or unexpected token is because either there is no key token in your localStorage yet or your value is improperly set.

1
  • Thanks! I double checked everything - also with the help of @Andrius: in my devtools > Application > LocalStorage I can see the key-values of token, expiresAt and userInfo. Everything looks right. It seems to me as if I have set the items in localStorage, but I am unable to get them. I think this is because of next.js? Any ideas what I could try to solve this?
    – Ewax_Du
    Commented Dec 28, 2020 at 17:11
1

useEffect hook runs on client side only. So, accessing storage inside it will guarantee that storage is defined. Here is a custom hook doing the work:

export default function useStorage(key, type = "sessionStorage") {
  const [value, setValue] = useState();

  // Initial fetch from storage
  useEffect(() => {
    const storage = type === "sessionStorage" ? sessionStorage : localStorage;
    setValue(storage.getItem(key));
  }, [key, type]);

  // Persist to storage
  useEffect(() => {
    // first render, don't override/destroy existing item value
    if (value !== undefined) {
      const storage = type === "sessionStorage" ? sessionStorage : localStorage;
      storage.setItem(key, value);
    }
  }, [key, value, type]);

  return [value, setValue];
}

Usage:

const [myValue, setMyValue] =  useStorage("my_value")
0

Next JS 13 with pages router

Below is the crux of how you can create/ remove/ update localStorage in Next JS.

const [token, setToken] = useState(null);

useEffect(() => {
  if (typeof window !== "undefined" && window.localStorage) {
    let token = localStorage.getItem("token");
    setToken(token);
  }
}, [setToken]);

const createToken = (newToken) => {
  if (typeof window !== "undefined" && window.localStorage) {
    localStorage.setItem("token", newToken);

    let token = localStorage.getItem("username");

    setToken(token);
  }
};

const removeToken = () => {
  if (typeof window !== "undefined" && window.localStorage) {
    localStorage.removeItem("token");
    setToken(null);
  }
};

const logout = useCallback(() => {
  removeToken();
}, []);

Reference

0

You can also consider importing this component AuthProvider using dynamic imports and setting the option ssr to false, Example

const AuthProvider=dynamic(()=>import("AuthProvider.tsx"),{ssr:false})

And using it normally as you would use this component elsewhere, just make sure it's dynamically imported. This worked for me.

Not the answer you're looking for? Browse other questions tagged or ask your own question.