React + Typescript === "headache"
Tags: typescript, react, webdev
Published: 2020-12-09 19:58:29

Hi, dear devs. 

Long time I haven't written anything here. I hope everybody is doing well.

Before you think I am here making criticism about the Typescript language, you are right! Naah, I am just kidding. My intention is just to describe my experience with the React + Typescript alliance. (it sounds like Star Wars :))

I won't explain in depth what **Typescript** is because there are tons of articles out there doing it, but for this article is enough knowing that it adds static typing to Javascript. [static typing x dynamic typing](https://somedudesays.com/2019/12/static-typing-vs-dynamic-typing/)

Since long ago I have been playing around with TS but actually I have never used it in any of my projects for a reason: TS suppose to be used in big applications, people said.

## But why should you also try it?
Like I mentioned, it is widely used in big applications so if you know **Typescript** you are already one step ahead of other candidates when applying for jobs. 😅

![AWS Experience](https://dev-to-uploads.s3.amazonaws.com/i/2ykxpih31d4ohktx0sin.jpeg) 

This time I have adventured myself into building a very small application using React + TS and below I related how it was. 

**PS.:** I expect you have at least the basics of React, hooks, CLI, axios and VSCode because I will focus in explaining just the TS features. 

## The adventure starts here. Enjoy the ride! 🎒

Installation takes several steps:

```
npm install --global typescript
```
```
npx create-react-app ts-react-app --template typescript
```
```
npm install --save axios
```

```
cd ts-react-app && code .
```
Our React application has a <code>tsconfig.json</code> file and the components have the <code>.tsx</code> extension.

If you see any difference between your application and mine it is because I have cleaned up and deleted a bunch of unnecessary files for the purpose of this article.

This is the so familiar React App file. Nothing new at the moment.
```typescript
import React from "react";
import PokemonContainer from "./components/PokemonContainer";

function App() {
  return (
    <div>
      <PokemonContainer />
    </div>
  );
}

export default App;

``` 
Inside of the components folder, we have the <code>PokemonContainer.tsx</code> file and its content:

```typescript
import React, { useState, useEffect } from "react";
import Pokemon from "../interfaces/Pokemon.interface";
import axios from "axios";

const PokemonContainer: React.FC = () => {
  const [pokemon, setPokemon]: [Pokemon, (pokemon: Pokemon) => void] = useState<Pokemon | null>(null);
  const [loading, setLoading]: [boolean, (loading: boolean) => void] = useState<
    boolean
  >(false);
  const [error, setError]: [string, (error: string) => void] = useState(
    "",
  );
  const [inputName, setInputName]: [string, (inputName: string) => void] = React
    .useState("bulbasaur");

  const pokemonRef: React.RefObject<HTMLInputElement> = React.createRef();

  const onSearchHandler = (): void => {
    setInputName(pokemonRef.current.value.toLowerCase());
  };

  useEffect(() => {
    setLoading(true);
    setError("");
    axios.get(`https://pokeapi.co/api/v2/pokemon/${inputName}`, {
      headers: {
        "Content-Type": "application/json",
      },
    })
      .then((response) => {
        setLoading(false);

        setPokemon(
          {
            name: response.data.name,
            base_experience: response.data.base_experience,
            imageURL: response.data.sprites.front_default,
            numberOfAbilities: response.data.abilities.length,
          },
        );
      })
      .catch((error) => {
        setLoading(false);
        setError(error.message);
      });
  }, [inputName]);

  return (
    <div>
      {loading && <div>Loading ...</div>}
      {error && <div>{error}</div>}
      {pokemon &&
        <div>
          <img src={pokemon.imageURL} alt="pokemon-pic" />
          <h3>{pokemon.name}</h3>
          <p>Base EXP: {pokemon.base_experience}</p>
          <p>Abilities: {pokemon.numberOfAbilities}</p>
        </div>}

      <div>Please, type the pokemon name below</div>
      <input type="text" ref={pokemonRef} />

      <button
        onClick={() => onSearchHandler()}
      >
        Search
      </button>
    </div>
  );
};

export default PokemonContainer;

```
## Super Overwhelming, right?!

Indeed but don't be intimidated for that. I am going to give my best to make you understand it. I promise it will worth your time. 

## Import Section
You don't really need me to explain it, do you? :)
```typescript
// It imports stuff haha
import React, { useState, useEffect } from "react";
import Pokemon from "../interfaces/Pokemon.interface";
import axios from "axios";
```

## Creating Our Functional Component
```typescript
const PokemonContainer: React.FC = () => { ... }
```
The line <code>: React.FC</code> indicates that our component **PokemonContainer** has the React **Functional** type (FC).

In Typescript you could define the types like: 
```typescript
let myName :string = "Vinicius"
let myAge :number = 99
```
but the correct way, in this case would be:
```typescript
let myName = "Vinicius"
let myAge = 99
```
because Typescript has the called [type inference](https://www.typescriptlang.org/docs/handbook/type-inference.html) on it.

(Thanks people in the comments from pointing it out. 😃)

Even tough, you are not only limited to those types you know. You can create your own types and interfaces. [types x interfaces](https://www.geeksforgeeks.org/what-is-the-difference-between-interface-and-type-in-typescript/)

## Interface
Coming from the <code>./src/interface/Pokemon.interface.ts</code> file:
```typescript
interface Pokemon {
  name: string;
  base_experience: number;
  numberOfAbilities: number;
  imageURL: string;
}

export default Pokemon;
```
We will fetch data from the [Pokemon API](https://pokeapi.co/docs/v2) using <code>axios</code>. The Pokemon interface we created explicitly defines the types of its fields. It avoids mistakes, for example, if for some reason someone tries to assign a number as the value for the pokemon name. 

## Typing the useState hooks values
```typescript
const [pokemon, setPokemon]: [Pokemon, (pokemon: Pokemon) => void] = useState<Pokemon | null>(null);
  const [loading, setLoading]: [boolean, (loading: boolean) => void] = useState<boolean>(false);
  const [error, setError]: [string, (error: string) => void] = useState("");
  const [inputName, setInputName]: [string, (inputName: string) => void] = React.useState("bulbasaur");
```
It looks more complicated than it really is. It is known that the <code>useState</code> hook returns the state and a function to change this state.
As an example I will explain the <code>const [pokemon, setPokemon]: [Pokemon, (pokemon: Pokemon) => void] = useState<Pokemon | null>(null);</code> statement. The explanation though applies to all the useState cases in this file.

- <code>[pokemon, ...]</code> is the state from the hook so its type is <code>pokemon: Pokemon</code>.
- <code>[..., setPokemon]</code> is the function responsible to change the state so it is type is <code>(pokemon: Pokemon) => void</code>. The <code>void</code> type indicates that the function doesn't return any value, it only **sets** the new state of the pokemon.
- <code>useState<Pokemon | null>(null);</code> indicates that the state the pokemon hook will receive is either of **Pokemon** or **null** types. The <code>|</code> (pipe) is equivalent an **OR** (<code>||</code>) condition. [Conditional Types](https://mariusschulz.com/blog/conditional-types-in-typescript)

## Create Ref
```typescript
const pokemonRef: React.RefObject<HTMLInputElement> = React.createRef();
```
I had no idea about how to typing a ref before (and believe you won't like to memorize all the existent types) but one cool feature is the **Typescript Intellisense** that suggests how you should type "stuff". It is not 100% accurate (yet) but it helps a lot.

## Last line of explanation 🏆 🎈 🎉 🍾 
```typescript
setPokemon({
            name: response.data.name,
            base_experience: response.data.base_experience,
            imageURL: response.data.sprites.front_default,
            numberOfAbilities: response.data.abilities.length,
          },
        );
```
It is not directly related to Typescript. I just want you to try something: assign the value of <code>base_experience</code> to <code>name</code> and vice-versa to see what happens. 😃

## Correction [EDITED x 2]
Thanks a lot for you guys that interacted with my article.
Our colleague [stereobooster](https://dev.to/stereobooster) created one amazing version of the same example that you can check below and also in the comments. 😊 

```typescript
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);
  // ...
}
```

## My Conclusion

Typescript is for sure a great language and it can avoid typing problems when developing applications already in the development phase. Now I see clearly why big companies have been adopting it in their projects. Spotting problems as soon as they come to existence can save lots of money and resources. 

On the other hand, it makes the development process slower if compared with the conventional **Javascript** mode. It also confuses a lot the newcomers (like me) that already have a background developing with Javascript.

I will definitely not choose TS in my personal projects given their size but as I mentioned previously, it worth having this knowledge.

**PS.:** This article just reflects my personal opinion about using Typescript and barely scratches the surface of it. 

If you really want to learn it, the [TS Official Documentation](https://www.typescriptlang.org/docs/) is the right place for you to go. 🗒️

Also here, as suggested in the comments [React TypeScript Cheatsheet](https://react-typescript-cheatsheet.netlify.app/). 🙌

Thanks for reach the end and Happy Hacking! 🙏

* Source code only for one's sake. [here](https://github.com/vinicius77/react-typescript-pokemon). 💻