loading...
Cover image for React Hook useState in TypeScript

React Hook useState in TypeScript

gabrielrufino profile image Gabriel Rufino ・3 min read

Typescript brought great evolution to the JavaScript and ReactJS ecosystem. More productivity, software more robust and reliable, interfaces, and error prediction during development are some advantages of use TypeScript in your project.

This is a post in English written by a Brazilian. I decided to try to write a series of articles in a new language with more reach than Portuguese using a translator eventually because I'm not fluent yet. So, I ask you for feedback and corrections if you find any errors. Thanks!

Here, I'll show you how to declare the type of a state when you use the React Hook useState.

First of all, look at the useState method description at the types file of the React API:

// ...
/**
 * Returns a stateful value, and a function to update it.
 *
 * @version 16.8.0
 * @see https://reactjs.org/docs/hooks-reference.html#usestate
 */
function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
// convenience overload when first argument is ommitted
/**
 * Returns a stateful value, and a function to update it.
 *
 * @version 16.8.0
 * @see https://reactjs.org/docs/hooks-reference.html#usestate
 */
function useState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];
// ...

Note that there are two definitions of the hook. The second definition overloads the first, giving the possibility of don't explicit the type of the state.

The main thing that you have note is that the method receives a TypeScript Generic called S. Through it, you can define the type of the state.

Look at these basic examples:

import React, {useState} from 'react'

export default function App() {
  const [name, setName] = useState<string>('Gabriel Rufino')
  const [age, setAge] = useState<number>(21)
  const [isProgrammer, setIsProgrammer] = useState<boolean>(true)

  return (
    <div>
      <ul>
        <li>Name: {name}</li>
        <li>Age: {age}</li>
        <li>Programmer: {isProgrammer ? 'Yes' : 'No'}</li>
      </ul>
    </div>
  )
}

If you try to set a state with a value that does not match the type, you'll cause an error:

import React, {useEffect, useState} from 'react'

export default function App() {
  const [name, setName] = useState<string>('Gabriel Rufino')
  const [age, setAge] = useState<number>(21)
  const [isProgrammer, setIsProgrammer] = useState<boolean>(true)

  useEffect(() => {
    // Error: Argument of type '21' is not assignable to parameter of type 'SetStateAction<string>'.ts(2345)
    setName(21)
    // Error: Argument of type 'true' is not assignable to parameter of type 'SetStateAction<number>'.ts(2345)
    setAge(true)
    // Error: Argument of type '"Gabriel Rufino"' is not assignable to parameter of type 'SetStateAction<boolean>'.
    setIsProgrammer('Gabriel Rufino')
  }, [])

  return (
    <div>
      <ul>
        <li>Name: {name}</li>
        <li>Age: {age}</li>
        <li>Programmer: {isProgrammer ? 'Yes' : 'No'}</li>
      </ul>
    </div>
  )
}

But for primary types, you don't need to make the type explicit, since typescript can infer them. Look:

import React, {useEffect, useState} from 'react'

export default function App() {
  const [name, setName] = useState('Gabriel Rufino')
  const [age, setAge] = useState(21)
  const [isProgrammer, setIsProgrammer] = useState(true)

  useEffect(() => {
    // Error: Argument of type '21' is not assignable to parameter of type 'SetStateAction<string>'.ts(2345)
    setName(21)
    // Error: Argument of type 'true' is not assignable to parameter of type 'SetStateAction<number>'.ts(2345)
    setAge(true)
    // Error: Argument of type '"Gabriel Rufino"' is not assignable to parameter of type 'SetStateAction<boolean>'.
    setIsProgrammer('Gabriel Rufino')
  }, [])

  return (
    <div>
      <ul>
        <li>Name: {name}</li>
        <li>Age: {age}</li>
        <li>Programmer: {isProgrammer ? 'Yes' : 'No'}</li>
      </ul>
    </div>
  )
}

The advantage comes when you store data more complex like objects or arrays. Suppose that we want to store an array of users like this:

[
  {
    "id": 1,
    "name": "Gabriel Rufino",
    "email": "contato@gabrielrufino.com"
  },
  {
    "id": 1,
    "name": "Darth Vader",
    "email": "darthvader@starwars.com"
  },
  {
    "id": 1,
    "name": "Luke Skywalker",
    "email": "lukeskywalker@starwars.com"
  }
]

We can define an interface that represents the format of a user. In this case, we should write some like:

interface IUser {
  id: number;
  name: string;
  email: string;
}

Now, we can write our component and put this data in a state with that type IUser[], that represents an array of objects with the format IUser:

import React, {useState} from 'react'

interface IUser {
  id: number;
  name: string;
  email: string;
}

export default function Users() {
  const [users, setUsers] = useState<IUser[]>([
    {
      id: 1,
      name: 'Gabriel Rufino',
      email: 'contato@gabrielrufino.com'
    },
    {
      id: 1,
      name: 'Darth Vader',
      email: 'darthvader@starwars.com'
    },
    {
      id: 1,
      name: 'Luke Skywalker',
      email: 'lukeskywalker@starwars.com'
    }
  ])

  return (
    <div>
      <ul>
        {users.map(user => (
          <li key={user.id}>{user.name} - {user.email}</li>
        ))}
      </ul>
    </div>
  )
}

But, this is usually not the way it works. Normally, we get data from an API asynchronously.

import React, {useState, useEffect} from 'react'
import axios from 'axios'

interface IUser {
  id: number;
  name: string;
  email: string;
}

export default function Users() {
  const [users, setUsers] = useState<IUser[]>([])

  useEffect(() => {
    axios.get<IUser[]>('https://api.yourservice.com/users')
      .then(({ data }) => {
        setUsers(data)
      })
  }, [])

  return (
    <div>
      <ul>
        {users.map((user: IUser) => (
          <li key={user.id}>{user.name} - {user.email}</li>
        ))}
      </ul>
    </div>
  )
}

Now you can use the setState in a more professional way.

Give me feedback.
Thanks!!

Posted on by:

gabrielrufino profile

Gabriel Rufino

@gabrielrufino

Fullstack Developer around JavaScript stack. Solving problems, generating value!

Discussion

pic
Editor guide
 

Muito bom, estava apanhando pra conseguir fazer isso, me ajudou muito, vlw!

 
 

Thank you!! :D