skip to Main Content

I am building one Project (To-Do-App) with login functionality,
In this Home component I have to check if token is not set in the localStorage, then navigate to login component,

But after the navigate, Home component still renders and in this component I have an API call, so because of the token not found in localStorage, it throws an error "Unauthorized"

Home Component:

import React, { useEffect } from "react";
import NotesContainer from "./NotesContainer";
import { useNavigate } from "react-router-dom";
import { toast } from "react-toastify";

function Home() {
    const navigate = useNavigate();

    useEffect(() => {
        if (!localStorage.getItem("token")) {
            navigate("/login");

            // for toast
            toast.error("Please login", {
                position: "top-right",
                autoClose: 5000,
                hideProgressBar: false,
                closeOnClick: true,
                pauseOnHover: true,
                draggable: true,
                progress: undefined,
                theme: "dark",
            });
        }
    }, []);

    return (
        <>
            <div className=" mt-4 px-2">
                <h1 className="text-4xl text-center font-semiboldbold underline underline-offset-2">
                    Your Notes
                </h1>
            </div>
            <div>
                <NotesContainer />
            </div>
        </>
    );
}

export default Home;

Inside NotesContainer Component I have API call.

NotesContainer:

import React, { useContext, useEffect } from "react";
import Note from "./Note";
import NoteModal from "./NoteModal";
import NoteContext from "../context/notes/NoteContext";

function NotesContainer() {
    const { notes, setAdding, setIsOpen, setCurrentNote, getNotes } =
        useContext(NoteContext);

    useEffect(() => {
        getNotes();
    }, []);

    return (
        <div className="px-3">
            <NoteModal />
            <div className="text-center mt-2 sm:text-end sm:mt-0npm st">
                <button
                    onClick={() => {
                        setCurrentNote({ title: "", description: "" });
                        setAdding(true);
                        setIsOpen(true);
                    }}
                    className="bg-slate-700 rounded-lg text-white text-md p-2 hover:scale-105"
                >
                    <span className="fas fa-plus "> </span>
                    &nbsp; Add New Note
                </button>
            </div>
            <div className="mt-3 flex justify-evenly flex-wrap gap-3">
                {notes.map((note) => (
                    <Note key={note._id} note={note} />
                ))}
            </div>
        </div>
    );
}

export default NotesContainer;

In current scenario, when I go to home component it will check for token, and if token is not set then it will navigate to Login component, but still home component is rendering and because of that my API call generate and throw an error.

enter image description here

So, I want that if token is not set in the localStorage then navigate to login before rendering the Home component so we can stop that API call.

3

Answers


  1. Rather than wait for your component to mount and run the effect hook (which is too late), use conditional rendering to render a <Navigate> component.

    This will avoid rendering your <NotesContainer> and thus any unauthenticated API calls.

    import { Navigate } from "react-router-dom";
    
    function Home() {
      // ...
    
      if (!localStorage.getItem("token")) {
        return <Navigate to="/login" replace />;
      }
    
      return (
        <>
          { /* ... */ }
        </>
      );
    }
    
    Login or Signup to reply.
  2. There are several options available to you to avoid the getNotes call in the NotesContainer component. Here are a few suggestions.

    1. Conditionally render the NotesContainer component only if there is a token value.

      import React, { useEffect } from "react";
      import NotesContainer from "./NotesContainer";
      import { useNavigate } from "react-router-dom";
      import { toast } from "react-toastify";
      
      function Home() {
        const navigate = useNavigate();
      
        const token = localStorage.getItem("token");
      
        useEffect(() => {
          if (!token) {
            navigate("/login");
      
            // for toast
            toast.error("Please login", { .... });
          }
        }, [token]);
      
        return (
          <>
            <div className=" mt-4 px-2">
              <h1 className="text-4xl text-center font-semiboldbold underline underline-offset-2">
                Your Notes
              </h1>
            </div>
            <div>
              {token && <NotesContainer />}
            </div>
          </>
        );
      }
      
      export default Home;
      
    2. Conditionally render the Home component’s content or null if there is no token.

      import React, { useEffect } from "react";
      import NotesContainer from "./NotesContainer";
      import { useNavigate } from "react-router-dom";
      import { toast } from "react-toastify";
      
      function Home() {
        const navigate = useNavigate();
      
        const token = localStorage.getItem("token");
      
        useEffect(() => {
          if (!token) {
            navigate("/login");
      
            // for toast
            toast.error("Please login", { .... });
          }
        }, [token]);
      
        if (!token) return null;
      
        return (
          <>
            <div className=" mt-4 px-2">
              <h1 className="text-4xl text-center font-semiboldbold underline underline-offset-2">
                Your Notes
              </h1>
            </div>
            <div>
              <NotesContainer />
            </div>
          </>
        );
      }
      
      export default Home;
      
    3. Conditionally render the Home component’s content or redirect if there is no token.

      import React, { useEffect } from "react";
      import NotesContainer from "./NotesContainer";
      import { useNavigate, Navigate } from "react-router-dom";
      import { toast } from "react-toastify";
      
      function Home() {
        const navigate = useNavigate();
      
        const token = localStorage.getItem("token");
      
        useEffect(() => {
          if (!token) {
            // for toast
            toast.error("Please login", { .... });
          }
        }, [token]);
      
        if (!token) return <Navigate to="/login" replace />;
      
        return (
          <>
            <div className=" mt-4 px-2">
              <h1 className="text-4xl text-center font-semiboldbold underline underline-offset-2">
                Your Notes
              </h1>
            </div>
            <div>
              <NotesContainer />
            </div>
          </>
        );
      }
      
      export default Home;
      
    4. Unconditionally render the Home component’s content, including the NotesContainer component, and check for the token in NotesContainer.

      import React, { useEffect } from "react";
      import NotesContainer from "./NotesContainer";
      import { useNavigate } from "react-router-dom";
      import { toast } from "react-toastify";
      
      function Home() {
        const navigate = useNavigate();
      
        const token = localStorage.getItem("token");
      
        useEffect(() => {
          if (!token) {
            navigate("/login");
      
            // for toast
            toast.error("Please login", { .... });
          }
        }, [token]);
      
        return (
          <>
            <div className=" mt-4 px-2">
              <h1 className="text-4xl text-center font-semiboldbold underline underline-offset-2">
                Your Notes
              </h1>
            </div>
            <div>
              <NotesContainer />
            </div>
          </>
        );
      }
      
      export default Home;
      
      function NotesContainer() {
        const {
          notes,
          setAdding,
          setIsOpen,
          setCurrentNote,
          getNotes
        } = useContext(NoteContext);
      
        useEffect(() => {
          const token = localStorage.getItem("token");
      
          if (token) {
            getNotes();
          }
        }, []);
      
        return (
          ....
        );
      }
      
    Login or Signup to reply.
  3. Make it simple and easy:

    Home Component:
    
     <>
          {!localStorage.getItem("token") 
           ?
           <>
                <Navigate to="/login"/>
           </> 
           : 
           <div className=" mt-4 px-2">
              <Home Component/>
           </div>
     </>
    

    It will be better to use Context API to make a global auth state

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search