Search code examples
reactjstypescriptasync-awaittry-catchhttp-error

Handling HTTP error with async await and try and catch clause in react typescript


I have the following code!

import React, { useState } from "react";

function App() {
const [movies, setMovies] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>();
 async function fetchMoviesHandler() {
    setIsLoading(true);
    setError(null);
try {
      const response = await fetch("https://swapi.dev/api/film/");

      const data = await response.json();
const transformedMovies = data.results.map((movieData: any) => {
        return {
          id: movieData.episode_id,
          title: movieData.title,
          openingText: movieData.opening_crawl,
          releaseDate: movieData.release_date,
        };
      });
setMovies(transformedMovies);
} catch (err: any) {
setError(err.Message);
      console.log(err);
    }
    setIsLoading(false);
  }
return (
    <React.Fragment>
      <section>
        <button onClick={fetchMoviesHandler}>Fetch Movies</button>
      </section>
      <section>
        
        {!isLoading && movies.length > 0 && <p>Movies fetched!</p>}
        {!isLoading && movies.length === 0 && !error && <p>Found no movies.</p>}
        {!isLoading && error && <p>{error}</p>}
        {isLoading && <p>Loading...</p>}
      </section>
    </React.Fragment>
  );
}

export default App;

Here the URL is incorrect and the 404 error would be thrown and I wanted that error message to be displayed. When I console.log the error message is displayed on the console but the error message is not displayed on the webpage. Would appreciate it if someone could help me out with this.


Solution

  • As you can see from the Fetch docs:

    The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500. Instead [...] the Promise will resolve normally (with the ok property of the response set to false if the response isn’t in the range 200–299)

    So the catch clause in fetchMoviesHandler doesn't get immediately executed when you get the 404 response, it only executes when you try to parse the body from the response (const data = await response.json()), as there's no json to parse there. That's when the error is actually thrown and catched, but then there's no Message property on it, so nothing to show: the property on the built-in error object is message.

    In order to handle this situation you should check for the ok property in the HTTP response and throw a specific error. Something like this:

    async function fetchMoviesHandler() {
        setIsLoading(true);
        setError(null);
        try {
          const response = await fetch('https://swapi.dev/api/film/');
          if (!response.ok) {
            throw new Error('Something went wrong');
          }
          const data = await response.json();
          const transformedMovies = data.results.map((movieData: any) => {
            return {
              id: movieData.episode_id,
              title: movieData.title,
              openingText: movieData.opening_crawl,
              releaseDate: movieData.release_date,
            };
          });
          setMovies(transformedMovies);
        } catch (err: any) {
          setError(err.message);
          console.log(err);
        }
        setIsLoading(false);
      }