Next.js Discord

Discord Forum

Localstorage with client component

Unanswered
Great black wasp posted this in #help-forum
Open in Discord
Great black waspOP
Hi, i want to fetch jwt token from localstorage and pass it to client component by useToken hook. What am i doing wrong? It just keeps me redirecting to /login
Any ideas?
"use client";

import { useHasMounted } from "@/hooks/useMountedHook";
import { useToken } from "@/hooks/useToken";
import { useRouter } from "next/navigation"; // Use "next/router" instead of "next/navigation"
import React, { useEffect } from "react";

function App() {
  const navigation = useRouter();
  const token = useToken();
  const mounted = useHasMounted();

  useEffect(() => {
    if (!mounted) return;

    if (!token) {
      navigation.push("/login");
    }
  }, [mounted, token]);

  return <div>{token}</div>;
}

export default App;

I will paste hook under question, in comment

4 Replies

Great black waspOP
import { apiUrl } from "@/constants";
import axios, { AxiosError } from "axios";
import { useState } from "react";

const useToken = () => {
  if (typeof window === "undefined") {
    return;
  }
  const [token, setToken] = useState<string | null>(null);
  const accessToken = localStorage.getItem("accessToken");
  const refreshToken = localStorage.getItem("refreshToken");

  if (refreshToken === null) return null;

  if (accessToken === null && refreshToken !== null) {
    //inline async function
    (async () => {
      try {
        const response = await axios.post(`${apiUrl}/account/refresh`, {
          refreshToken,
        });
        if (response.status === 200) {
          setToken(response.data.token);
          localStorage.setItem("accessToken", response.data.token);
        }
      } catch (err) {
        return null;
      }
    })();
  }

  if (accessToken !== null) {
    (async () => {
      try {
        const response = await axios.get(`${apiUrl}/account`, {
          headers: {
            Authorization: `Bearer ${accessToken}`,
          },
        });
        if (response.status === 200) {
          setToken(accessToken);
        }
      } catch (err) {
        const error = err as AxiosError;
        if (error.response?.status === 401) {
          localStorage.removeItem("accessToken");
          const response = await axios.post(`${apiUrl}/account/refresh`, {
            refreshToken,
          });
          if (response.status === 200) {
            localStorage.setItem("accessToken", response.data.token);
            setToken(response.data.token);
          }
        }
      }
    })();
  }

  return token;
};

export { useToken };
1. you shouldn't call hooks conditionally
2. side effects should be done inside useEffect, not in the root of hooks
3. you can't access localStorage in the root of a hook since it runs in the server, you must do it inside useEffect as it only runs in the browser
4. useEffect is called after the component is mounted, this check for useHasMounted doesn't really make sense
Great black waspOP
2. So i should not create that hook (useToken)?
you can as long as you follow the rule of hooks, you need to add at least one useEffect to your custom hook to properly handle this logic