How to stop loading page until response is received from REST endpoint in React?

Viewed 469

I have the following function in my ReactJS app:

function MyView() {

 
    const [mydata, setMyData] = useState({ mydata: {} });
    const [mystatus, setMyStatus] = useState({ mystatus: null });
 
    useEffect(() => {
        let config = {
            headers: {
                'Authorization': 'Bearer ' + localStorage.getItem('my-token'),
                'Content-Type': 'application/json'
            }
          }

      const fetchData = async () => {
        const result = await axios(
            'http://localhost:8000/endpoint/' + some_id + "/",
            config
        );
   
        setMyStatus(result.status);
        setMyData(result.data);
      };
   
      fetchData();
    }, []);

    .... <Loading the rest of functions and HTML here>

I want to make sure that the mystatus is 200 and that mydata.view == public before I load anything here. The problem is that JS loads rest of the page before the response is received from server.

How do I stop it from doing that i.e, redirect to another page if mystatus !== 200 or mydata.view !== 'public'

4 Answers

You can conditionally render the react component based on the value of the components states mystatus and mydata.

  const [mydata, setMyData] = useState(null);
  const [mystatus, setMyStatus] = useState(null);
  const history = useHistory();

  useEffect(() => {
    let config = {
      headers: {
        Authorization: 'Bearer ' + localStorage.getItem('my-token'),
        'Content-Type': 'application/json',
      },
    };

    const fetchData = async () => {
      const result = await axios(
        'http://localhost:8000/endpoint/' + some_id + '/',
        config
      );

      setMyStatus(result.status);
      setMyData(result.data);
    };

    fetchData();
  }, []);

  if (!mystatus || !mydata) {
    return <h1>Loading....</h1>;
  }
  if (
    (mystatus?.status !== 200) ||
    (mydata?.view !== 'public')
  ) {
    history.push('/redirect-to-your-public-route');
  }

  // .... <Loading the rest of functions and HTML here>

You can't at the moment, even if there is a way it will be kind of hacky.

Instead, you can consider rendering a placeholder/skeleton first similar to this: enter image description here Then, when the data is ready and verified, you replace the placeholder/skeleton with actual content.

As for error handling, you can do something like this:

const fetchData = async () => {
  try {
    const result = await axios(
      'http://localhost:8000/endpoint/' + some_id + "/",
      config
    );

    setMyStatus(result.status);
    setMyData(result.data);
  } catch (error) {
    // do something
    // redirect to the corresponding page
  }
};

Note, the upcoming concurrent mode (still experimental and have no ETA, but this has been discussed for a long time) will change the practice diastically.

You can have a boolean stating if it can show page or not and only show if that is true.

const [canShow, setCanShow] = useState(false);

Then set it to true if all the requirements are met.

const fetchData = async () => {
    const result = await axios(
        'http://localhost:8000/endpoint/' + some_id + "/",
        config
    );

    if(result.status === 200 && result.data.view === "public")
        setCanShow(true)

    setMyData(result.data);
  };

Then in the return of your function have a check to see if you show the page or a loading screen or whatever you want.

return(
    {canShow ? /* display whatever page it should show */ : /* show loading page or whatever else */}
}

You can't stop loading, instead return null until mystatus and mydata.view will satisfy the condition.

function MyView() {
  const [mydata, setMyData] = useState();
  const [mystatus, setMyStatus] = useState();
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let config = {
      headers: {
        'Authorization': 'Bearer ' + localStorage.getItem('my-token'),
        'Content-Type': 'application/json'
      }
    }

    const fetchData = async () => {
      const result = await axios(
        'http://localhost:8000/endpoint/' + some_id + "/",
        config
      );

      if (result.status !== 200 || result.data.myview !== "public") {
        window.location.href = "your_redirect_url"
      }

      setMyStatus(result.status);
      setMyData(result.data);
      setLoading(false);
    };

    fetchData();
  }, []);

  if (loading) {
    return <>Loading...</>
  }
  if (mystatus !== 200 || mydata.myview !== "public") {
    return null;
  }
  return // rest of your code
}
Related