skip to Main Content

I am trying to render profile page is the user has not completed the profile by getting profile data from an API call. If the data is complete, I want to redirect the user to landing page.

I got the part where if the data is missing, the app redirects to profile page everytime on every page. Once the data is completed and persisted, it stops the redirect. However, on refresh it again redirect to profile page ONCE and then behaves properly.

I suspect this is because the first render has no data and as result triggers to the redirect.

How do I avoid that redirect?

const [isProfileComplete, setIsProfileComplete] = useState(true);
const [userData, setUserData] = useState({
        email: "[email protected]",
        fName: "firstName",
        lName: "lastName",
    });

useEffect(()=>{
        let url = 'public/getUserByEmail?email='+userData.email;
        client.get(url).then((response) => {
            console.log(response);
            if(response.status === 200){
                setUserData(response.data);
            }
        })
        .catch((error) => {
            console.log(error);
        });

},[])

useEffect(()=>{
   checkIfProfileComplete();

},[userData])

function checkIfProfileComplete(){

        const isProfileEmpty = (
            userData.email.trim() === "" ||
            userData.fName.trim() === "" ||
            userData.lName.trim() === "" ||
        );

        if(isProfileEmpty){
            //navigate
            let path = "/app/account/profile/";
            navigate(path);
        }else{
            let path = "/app/dashboard/";
            navigate(path);
        }
}


I know the problem is I am waiting for API call but useEffect is called with empty data and hence I redirected. How do I solve this?

3

Answers


  1. The issue you are facing is likely due to the initial state of userData, which contains default values for email, fName, and lName. Since you are making an API call to fetch the user data in the first useEffect, there is a short period when userData contains the default values, and the checkIfProfileComplete function is triggered, leading to the redirect.

    To avoid this initial redirect, you can modify your useEffect to check if the user data is complete only after the API call has finished and updated the userData state. You can achieve this by using another state variable to track whether the API call has been completed.

    Here’s an updated version of your code:

    const [isProfileComplete, setIsProfileComplete] = useState(true);
    const [userData, setUserData] = useState({
      email: "[email protected]",
      fName: "firstName",
      lName: "lastName",
    });
    const [apiCallComplete, setApiCallComplete] = useState(false); // New state variable
    
    useEffect(() => {
      let url = 'public/getUserByEmail?email=' + userData.email;
      client
        .get(url)
        .then((response) => {
          console.log(response);
          if (response.status === 200) {
            setUserData(response.data);
          }
        })
        .catch((error) => {
          console.log(error);
        })
        .finally(() => {
          // Mark the API call as complete, regardless of success or error
          setApiCallComplete(true);
        });
    }, []);
    
    useEffect(() => {
      if (apiCallComplete) {
        checkIfProfileComplete();
      }
    }, [userData, apiCallComplete]);
    
    function checkIfProfileComplete() {
      const isProfileEmpty = userData.email.trim() === "" || userData.fName.trim() === "" || userData.lName.trim() === "";
    
      if (isProfileEmpty) {
        //navigate
        let path = "/app/account/profile/";
        navigate(path);
      } else {
        let path = "/app/dashboard/";
        navigate(path);
      }
    }

    With this change, the checkIfProfileComplete function will be called only after the API call is completed, ensuring that userData contains the correct values fetched from the server before checking if the profile is complete. This should prevent the initial redirect on page load.

    Login or Signup to reply.
  2. A typical way to solve this problem is to introduce a loading state that represents the fact that you’re still waiting for the data to arrive. You can then skip the redirect logic if you’re in this loading state.

    const [isProfileComplete, setIsProfileComplete] = useState(true);
    const [userData, setUserData] = useState({
        email: "[email protected]",
        fName: "firstName",
        lName: "lastName",
    });
    const [isLoading, setIsLoading] = useState(true);
    
    useEffect(() => {
        let url = 'public/getUserByEmail?email=' + userData.email;
        client.get(url).then((response) => {
            console.log(response);
            if (response.status === 200) {
                setUserData(response.data);
            }
        })
            .catch((error) => {
                console.log(error);
            })
            .finally(() => {
                setIsLoading(false);
            });
    
    }, []);
    
    useEffect(() => {
        if (!isLoading) {
            checkIfProfileComplete();
        }
    }, [userData, isLoading]);
    
    function checkIfProfileComplete() {
    
        const isProfileEmpty = (
            userData.email.trim() === "" ||
            userData.fName.trim() === "" ||
            userData.lName.trim() === "" ||
        );
    
        if (isProfileEmpty) {
            //navigate
            let path = "/app/account/profile/";
            navigate(path);
        } else {
            let path = "/app/dashboard/";
            navigate(path);
        }
    }
    

    isLoading state variable is initially set to true, and is set to false in the .finally() block of your API call. Then, the second useEffect hook checks isLoading before calling checkIfProfileComplete(), so the redirect logic will be skipped until the data has loaded.

    This approach ensures that the redirect logic doesn’t run until the API call is complete, and the loading state accurately represents whether you’re still waiting for the data. You can also use this loading state to render a loading spinner or other placeholder content while you’re waiting for the data if you want to.

    Login or Signup to reply.
  3. There is no persistence through refresh in your code. Following the best practice about this common mistake, if you want to be able to get the data even after a refresh without having to recall the api (Which should be what’s happenning after reading your question), you will need to use the local storage to persist the data and then check if it exists when accessing your app.
    For this you could use a guard component that would wrap your app, something like :

    root.render(
      <AuthGuard>
        <App />
      </AuthGuard>
    );
    

    and this Authguard would do the redirection and check for the data in the local storage. It is very easy to use, here is the documentation.

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