DEV Community

Discussion on: React + Typescript === "headache"

Collapse
 
vinicius77 profile image
Vinicius Cerqueira Bonifácio

Thanks a lot for your comment and insights. :)

I will definitively adopt your approach and also refactor the code following your suggestions.

Collapse
 
stereobooster profile image
stereobooster • Edited
import React, { useState, useEffect } from "react";
import axios from "axios";

type Pokemon = {
  name: string,
  base_experience: number,
  numberOfAbilities: number,
  imageURL: string
}

type UseGet<Data> = {
    data: Data | undefined,
    loading: boolean,
    error: string  | undefined
}

const useGetPokemon = (name: string) => {
  const [state, setState] = useState<UseGet<Pokemon>>({
    loading: false,
    error: undefined
    data: undefined,
  });

  useEffect(() => {
    const source = axios.CancelToken.source();
    const cancelToken = source.token;
    setState({
      loading: true,
      error: undefined,
      data: undefined,
    });
    axios.get(`https://pokeapi.co/api/v2/pokemon/${name}`, {
        cancelToken,
        headers: { "Content-Type": "application/json" },
      }).then(({ data }) => {
        setState({
          loading: false,
          error: undefined,
          data: {
            name: data.name,
            base_experience: data.base_experience,
            imageURL: data.sprites.front_default,
            numberOfAbilities: data.abilities.length,
          },
        });
      }).catch((error) => {
        if (!axios.isCancel(error)) {
          setState({
            loading: false,
            error: error.message,
            data: undefined,
          });
        }
    });
    return () => source.cancel();
  }, [name, setState]);

  return state;
}

const PokemonSearch: React.FC = () => {
  const [inputName, setInputName] = React.useState("bulbasaur");
  const { data: pokemon, error, loading } = useGetPokemon(inputName);
  // ...
}
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
vinicius77 profile image
Vinicius Cerqueira Bonifácio • Edited

What could I say? You definitively nailed it! 👏

I have to admit that reading your code brought me some kind of satisfaction.

Let's agree (or not) that being my first time trying TS I was not that bad, I guess. 😅
Also in my defense I didn't invest too much time in other parts of the code but sir, let's be honest here, your version rocks!

By the way, may I ask you one question?
I have noticed the way you created the cancellation token and I would like to know it the approach below is also right because I have been using it since always. I mean, regarding good practices.

useEffect(() => {
    let cancel; 
    axios
        .get(`https://pokeapi.co/api/v2/pokemon/${name}`, {
        cancelToken: new axios.CancelToken((ctoken) => (cancel = ctoken)),
      })
    //...
    return () => cancel();
}
Enter fullscreen mode Exit fullscreen mode

Thanks again for participating, I really appreciate it! 🙏

Thread Thread
 
stereobooster profile image
stereobooster • Edited

I never meant it as a challenge. I know that using TS is hard and wanted to show alternative approach.

Regarding cancel question: theoretically provided code can go wrong because of asynchronous functions, but probably in practice you will never see problems. In your code TS will complain about cancel being used before assignment (probably)

Thread Thread
 
vinicius77 profile image
Vinicius Cerqueira Bonifácio

Please, don't get me wrong. It was not a challenge at all.

I really just wanted to report my first experience with TS but I think it is important to highlight the alternative you provided, for people like me who are giving the first steps with the language. For example, I'll surely use it as a reference and probably other people too.

Thanks for the explanation about the cancelling token. Until now I have had no problems but considering changing the way I use it. :)

I have learned a lot from the example you provided! Thanks for that.