Vivek
Vivek

Reputation: 13288

How to navigate based on state using react router

In my react project, I'm using fetch api to get the user profile from backend. If there is any error occurred in the API call I'm showing it on the screen.

const navigate = useNavigate();
const [errorMessage, setErrorMessage] = React.useState("");

...

const handleGetProfile = async () => {
  await fetch(`${API_URL}/profile`).then(...).catch(err=>setErrorMessage(err.message))
  !errorMessage && navigate("/");
}

I wanted to navigate to root path only if no error occurred in the api call. So I'm checking if the error is empty and navigating to the root path.

The problem with this approach is that the setErrorMessage does not guarantee immediate update because it schedules the state update, so it is always navigating to the root path even if there is an error.

How do I solve this issue, any suggestions?

Upvotes: 1

Views: 1070

Answers (1)

Drew Reese
Drew Reese

Reputation: 203323

Correct, because React state updates are asynchronously processed, and treated as const the errorMessage state won't have updated inside the handleGetProfile callback.

const handleGetProfile = async () => {
  await fetch(`${API_URL}/profile`)
    .then(...)
    .catch(err => setErrorMessage(err.message));
  !errorMessage && navigate("/");
}

It's also anti-pattern to mix async/await with Promise chains. Generally you use one or the other.

To resolve you should move the navigate call into the "resolved" part of the logic. Since fetch returns a Promise and only rejects on network errors you need to also check the response status.

See Checking that the fetch was successful

A fetch() promise will reject with a TypeError when a network error is encountered or CORS is misconfigured on the server-side, although this usually means permission issues or similar — a 404 does not constitute a network error, for example. An accurate check for a successful fetch() would include checking that the promise resolved, then checking that the Response.ok property has a value of true. The code would look something like this:

fetch('flowers.jpg')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not OK');
    }
    return response.blob();
  })
  .then(myBlob => {
    myImage.src = URL.createObjectURL(myBlob);
  })
  .catch(error => {
    console.error('There has been a problem with your fetch operation:', error);
  });
  • Using Promise chain

    const handleGetProfile = () => {
      fetch(`${API_URL}/profile`)
        .then((response) => {
          if (!response.ok) {
            throw new Error('Network response was not OK');
          }
          // handle any successful response stuff
          navigate("/");
        })
        .catch(err => {
          setErrorMessage(err.message || err);
        });
    }
    
  • Using async/await with try/catch

    const handleGetProfile = async () => {
      try {
        const response = await fetch(`${API_URL}/profile`);
        if (!response.ok) {
          throw new Error('Network response was not OK');
        }
        // handle any successful response stuff
        navigate("/");
      } catch(err) {
        setErrorMessage(err.message || err);
      }
    }
    
  • Use an useEffect hook to response to state changes

    const navigate = useNavigate();
    const [isFetched, setIsFetched] = React.useState(false);
    const [errorMessage, setErrorMessage] = React.useState("");
    
    useEffect(() => {
      if (isFetched && !errorMessage) {
        navigate("/");
      }
    }, [errorMessage, isFetched, navigate]);
    
    ...
    
    const handleGetProfile = async () => {
      setErrorMessage(null);
      setIsFetched(false);
      try {
        const response = await fetch(`${API_URL}/profile`);
        if (!response.ok) {
          throw new Error('Network response was not OK');
        }
        // handle any successful response stuff
        navigate("/");
      } catch(err) {
        setErrorMessage(err.message || err);
      } finally {
        setIsFetched(true);
      }
    }
    

Upvotes: 3

Related Questions