Home > Net >  Handling API Error responses in React Typescript App
Handling API Error responses in React Typescript App

Time:11-22

I am trying to learn React and Typescript. I am building a little demo app that sends a request to the api App with dev console on initial load

How can I get this to work the way I want it to? Any help would be much appreciated. If a moderator deletes this question, can they please let me know why? Thanks.

import React, {FormEvent, useEffect, useState} from "react";
import { useForm } from 'react-hook-form'
import "./App.css";
import axios from "axios";
import { IPlace } from "./IPlace";

export default function App2(){
    const [placeFound, setPlaceFound] = useState<IPlace[]>([]);
    const [zipSearch, setZipSearch] = useState("");
    const [errorFound, setErrorFound] = React.useState("");
  

    const renderPlaces = () => {
        console.log("Render places runs")

        if(placeFound.length !== 0){
            return (<div className="table-container">
            <table>
                <thead>
                    <tr>
                        <th><span>State</span></th>
                        <th><span>Longitude</span></th>
                        <th><span>Latitude</span></th>
                        <th><span>Place Name</span></th>
                    </tr>
                </thead>
                {placeFound.map((place) =>{
                return (
                    <tbody>
                        <tr>
                            <td>{place.state}</td>
                            <td>{place.longitude}</td>
                            <td>{place.latitude}</td>
                            <td>{place["place name"]}</td>
                        </tr>
                    </tbody>
                    )})}
            </table>
        </div>)
        }
        
        
    }

    React.useEffect(() => {
        console.log("useEffect is run")
        const query = encodeURIComponent(zipSearch);
        
        axios
          .get(`https://api.zippopotam.us/us/${query}`,{
          })
          .then((response) => {
            setPlaceFound(response.data.places);
            setErrorFound("");
          })
          .catch((ex) => {
            let errorFound = axios.isCancel(ex)
              ? 'Request Cancelled'
              : ex.code === 'ECONNABORTED'
              ? 'A timeout has occurred'
              : ex.response.status === 404
              ? 'Resource Not Found'
              : 'An unexpected error has occurred';
            setErrorFound(ex.code);
            setPlaceFound([]);
          });
          
        }
        
      },[zipSearch]);

    const search=(event: FormEvent<HTMLFormElement>) =>{
        console.log("Search method runs")
        event.preventDefault();
        const form = event.target as HTMLFormElement;
        const input = form.querySelector('#zipSearchInput') as HTMLInputElement;
        setZipSearch(input.value);
    }

    return (
        <div className="App">
            <div className="search-container">
                <h1>Place Search using Zip Code</h1>
                <form className="searchForm" onSubmit={event => search(event)}>
                    <div>
                        <label htmlFor="zipSearchInput">Zip Code</label>
                        <input {...register('zipSearchInput', { required: true, minLength: 5, maxLength: 5 }) }
                        id="zipSearchInput" 
                        name="zipSearchInput" 
                        type="text"
                        />
                    </div>                
                    {
                        errors.zipSearchInput && <div className="error">Zip Code is required and must be 5 digits long</div>
                    }
                    <button type="submit">Search</button>
                </form>
            </div>
            {placeFound.length !== 0 && renderPlaces()}
            {errorFound !== "" && <p className="error">{errorFound}</p>}
    </div>)
}

CodePudding user response:

What you should probably do is actually use the library above react-hook-form and its functions.

Then I believe that the useEffect that you are using is pretty useless in this scenario, You are going the long way for a simpler task. You can simply call the api on submit of the form and get rid of the state zipSearch the react-hook-form is taking care of that for you.

Here's the fixed version of the code below:

import React, {  useState } from "react";
import { FormProvider, useForm } from "react-hook-form";
import axios from "axios";
interface IPlace {
  state: string;
  longitude: string;
  latitude: string;
  "place name": string;
}
export default function App2() {
  const [placeFound, setPlaceFound] = useState<IPlace[]>([]);
  const [errorFound, setErrorFound] = React.useState("");
  const formMethods = useForm<{ zipSearchInput: string }>();

  const renderPlaces = () => {
    console.log("Render places runs");

    if (placeFound.length !== 0) {
      return (
        <div className="table-container">
          <table>
            <thead>
              <tr>
                <th>
                  <span>State</span>
                </th>
                <th>
                  <span>Longitude</span>
                </th>
                <th>
                  <span>Latitude</span>
                </th>
                <th>
                  <span>Place Name</span>
                </th>
              </tr>
            </thead>
            {placeFound.map((place) => {
              console.log(place);
              
              return (
                <tbody key={place.latitude}>
                  <tr>
                    <td>{place.state}</td>
                    <td>{place.longitude}</td>
                    <td>{place.latitude}</td>
                    <td>{place["place name"]}</td>
                  </tr>
                </tbody>
              );
            })}
          </table>
        </div>
      );
    }
  };


  const search = (values: { zipSearchInput: string }) => {
    console.log(values);
    const query = encodeURIComponent(values.zipSearchInput);

    axios
      .get(`https://api.zippopotam.us/us/${query}`, {})
      .then((response) => {
        setPlaceFound(response.data.places);
        setErrorFound("");
      })
      .catch((ex) => {
        let _errorFound = axios.isCancel(ex)
          ? "Request Cancelled"
          : ex.code === "ECONNABORTED"
          ? "A timeout has occurred"
          : ex.response.status === 404
          ? "Resource Not Found"
          : "An unexpected error has occurred";
        setErrorFound(_errorFound);
        setPlaceFound([]);
      });
  };

  return (
    <div className="App">
      <div className="search-container">
        <h1>Place Search using Zip Code</h1>
        <FormProvider {...formMethods}>
          <form className="searchForm" onSubmit={formMethods.handleSubmit(search)}>
            <div>
              <label htmlFor="zipSearchInput">Zip Code</label>
              <input
                {...formMethods.register("zipSearchInput", {
                  required: true,
                  minLength: 5,
                  maxLength: 5
                })}
                id="zipSearchInput"
                name="zipSearchInput"
                type="text"
              />
            </div>
            <button type="submit">Search</button>
          </form>
        </FormProvider>
      </div>
      {placeFound.length !== 0 && renderPlaces()}
      {errorFound !== "" && <p className="error">{errorFound}</p>}
    </div>
  );
}

Good Luck

  • Related