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!

Ewax_Du
  • 325
  • 1
  • 8
  • 22
  • 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 – Andrius Solopovas Dec 28 '20 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 Dec 28 '20 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? – Andrius Solopovas Dec 28 '20 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 Dec 28 '20 at 18:34
  • I found also found this - maybe this could be helpful: https://stackoverflow.com/questions/59540321/in-reactjs-and-nextjs-constructor-getting-reference-error-localstorage-is-not-d – Ewax_Du Dec 28 '20 at 18:36
  • I have not built any application with next.js, I have built apps on [nuxt.js](https://nuxtjs.org/) I could use ` if (process.server) ` to make sure that code is rendered on server side, the opposite could be done like so `if (!process.server)`. They also hade a specific method for [data fetching](https://nuxtjs.org/docs/2.x/features/data-fetching) on ssr for components. You should seek equivalent. – Andrius Solopovas Dec 28 '20 at 18:45

3 Answers3

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..

Ewax_Du
  • 325
  • 1
  • 8
  • 22
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.

kennysliding
  • 2,783
  • 1
  • 10
  • 31
  • 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 Dec 28 '20 at 17:11
0

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")
Elnoor
  • 3,401
  • 4
  • 24
  • 39