React useState Hook Typescript

React useState Hook Typescript

The Anatomy of the useState Hook

To use the useState hook, you need to know a few things. You can check the figure below to better understand what I’ll explain here.

  1. You must import it from the React library.
  2. You must invoke it inside a React component
const array = useState(initialValue)Code language: JavaScript (javascript)

Here we can initialize an array and then access the elements using the array[0] and array[1]. We can use the array[0] element as state and the array[1] element as setState.This is based on a principle in javascript called the destructuring assignment. The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.

const [state, setState] = useState(initialValue)Code language: JavaScript (javascript)

3.  You can then freely render state, or call setState to update your state value.

import {useState} from 'react'

function App{
const [state, setState] = useState(1)

return(
<section>
<div>{state}</div>
<button onClick={() => setState(state + 1)}>More
</button>
<button onClick={() => setState(state - 1)}>Less
</button>
</section>
)
}Code language: JavaScript (javascript)

To define a type React.useState() you must add an <type> after typing the word useState and before the opening parenthesis.

Using typescript

Typescript

Typescript brought great evolution to the JavaScript and ReactJS ecosystem. More productivity, software more robust and reliable, interfaces, and error prediction during development are achievable using TypeScript.

To type the useState hook as an object in React, use the hook’s generic, e.g. const [employee, setEmployee] = useState<{name: string; salary: number}>({name: '',salary: 0}) The state variable will only accept key-value pairs of the specified type.

import {useEffect, useState} from 'react';

const App = () => {
//const employee: {name: string;salary: number;}
  const [employee, setEmployee] = 
useState<{name: string; salary: number}>({
    name: '',
    salary: 0,
  });

  useEffect(() => {
    setEmployee({name: 'James', salary: 100});
  }, []);

  return (
    <div>
      <h2>Name: {employee.name}</h2>
      <h2>Salary: {employee.salary}</h2>
    </div>
  );
};

export default App;
Code language: JavaScript (javascript)

We used a generic to type the useState hook correctly while initializing the hook with an object.

Sometimes you might not want to set initial values for all of the object’s properties. In this case, you can mark the properties as optional.

We used a question mark to mark the salary property as optional.

The property can either store an undefined value or a value of type number.

const App = () => {
  // mark salary as optional
  const [employee, setEmployee] = useState<{
    name: string; salary?: number
  }>({
    name: '',
  });Code language: JavaScript (javascript)

This is why we do not provide it when initializing the state object. If you provide initial values for all of your object’s properties, TypeScript will be able to infer the type of the state variable.

import {useEffect, useState} from 'react';

const App = () => {
  //  const employee: {name: string;salary: number;}
  //  typed correctly without a generic
  const [employee, setEmployee] = useState({
    name: '',
    salary: 0,
  });

  useEffect(() => {
    setEmployee({name: 'James', salary: 100});
  }, []);

  return (
    <div>
      <h2>Name: {employee.name}</h2>
      <h2>Salary: {employee.salary}</h2>
    </div>
  );
};

export default App;
Code language: JavaScript (javascript)

We passed initial values for all of the object’s properties, which enabled TypeScript to type the employee variable correctly. However, it is a best practice to always explicitly type the useState hook, especially when working with arrays and objects. In some cases, you might not know all of the properties you will set on the object in advance.

import {useEffect, useState} from 'react';

const App = () => {
  //  flexible object type
  const [employee, setEmployee] = 
useState<{[key: string]: any}>({});

  useEffect(() => {
    setEmployee({
      name: 'James',
      salary: 100,
      department: 'Dev',
      tasks: ['dev', 'test', 'ship'],
    });
  }, []);

  return (
    <div>
      <h2>Name: {employee.name}</h2>
      <h2>Salary: {employee.salary}</h2>
    </div>
  );
};

export default App;
Code language: JavaScript (javascript)

The {[key: string]: any} the syntax is an index signature in TypeScript and is used when we don’t know all the names of a type’s properties and the shape of the values ahead of time.

The index signature in the example means that when the object is indexed with a string, it will return a value of any type.

You can use this approach when you don’t know all of the object’s properties in advance.

If you want to set an object property to be one of the multiple types, use a union.

import {useEffect, useState} from 'react';

const App = () => {
  const [employee, setEmployee] = useState<{
    name: string;
    //  string OR number
    salary: string | number;
  }>({
    name: '',
    salary: '',
  });

  useEffect(() => {
    setEmployee({name: 'James', salary: 100});
  }, []);

  return (
    <div>
      <h2>Name: {employee.name}</h2>
      <h2>Salary: {employee.salary}</h2>
    </div>
  );
};

export default App;
Code language: JavaScript (javascript)

We used a union to set the salary property to be of type string or number.

If your useState hook gets busy, extract the type you pass to the generic into a type alias or an interface.

import {useEffect, useState} from 'react';

type Employee = {
  name: string;
  salary: number;
};

const App = () => {
  //const employee: {name: string; salary: number;}
  const [employee, setEmployee] = 
useState<Employee>({
    name: '',
    salary: 0,
  });

  useEffect(() => {
    setEmployee({name: 'James', salary: 100});
  }, []);

  return (
    <div>
      <h2>Name: {employee.name}</h2>
      <h2>Salary: {employee.salary}</h2>
    </div>
  );
};

export default App;
Code language: JavaScript (javascript)

The React useState Hook Definition

The React useState hook plays an important part when using functional components, from storing temporary data to receiving data from an API call. With the introduction of TypeScript, developer experience has increased manifold.

TypeScript has support for adding types to React useState hook. This is advantageous as TypeScript can infer the types during setting the values and even detect errors in types.

We can mitigate this before to allow safe deployment.

TypeScript has a generic definition for React useState according to the TypeScript React documentations.

function useState<S>(initialState: S | (() => S)): 
[S, Dispatch<SetStateAction<S>>];
Code language: JavaScript (javascript)

Here, S is a generic type. It accepts an initial state, and the hook can accept the state indicated by S , a setState function of type Dispatch<SetStateAction<S>>.

Use Primitive Types With React useState Hook in TypeScript

The useState hook can set primitive types in the application state. The primitive types include numberstring, and boolean.

Here is an example of how useState can be used for primitive types in TypeScript.

const InfoComponent = () => {

    const [name, setName] = 
    React.useState<string>("");
    const [age, setAge] = 
    React.useState<number>(0);
    const [isMarried, setIsMarried] = 
    React.useState<boolean>(false);


    React.useEffect(() => {
        setName("Geralt");
        setAge(95);
        setIsMarried(false);
    }, []);

    return (
        <>
          <div>Witcher name : {name}</div>
          <div>Age : {age}</div>
          <div>Married : {isMarried ? 'Yes' : 'No'}
          </div>
        </>
    )
}Code language: JavaScript (javascript)

Thus the useState hook has been used for storing the primitive types which are being set in a useEffect hook that gets triggered once when the component is mounted.

Use User-Defined Interfaces to the Store States in TypeScript

Even user-defined interfaces can be used as a type for the useState hook. We can modify the code segment used in the previous section to store the information in an interface so that the code becomes more organized.

interface IUser {
    name : string ;
    age : number ;
    isMarried : boolean ;
}
const InfoComponent = () => {

    const [ state, setState ] = 
React.useState<IUser>({
        name : "",
        age : 0,
        isMarried : false
    });

    React.useEffect(() => {
        setState({
            name : "Geralt",
            age : 95,
            isMarried : false
        });
    }, []);

    return (
        <>
        <div>Witcher name : {state.name}</div>
        <div>Age : {state.age}</div>
        <div>Married : 
             {state.isMarried ? 'Yes' : 'No'}
        </div>
        </>
    )
}Code language: JavaScript (javascript)

For setting optional fields in the setState function, the as the keyword can be used for type assertion. We must override the initial state with the optional state attributes passing to the setState function.

setState({
    ...state, ...{
        name : "Geralt",
        isMarried : "false"
    } as unknown as IUser
});Code language: JavaScript (javascript)

Thus in the above example, the age field is not set, and the default value is used, which is provided by the state. The state override is done by the (...) or the spread operator.

Use Arrays in useState Hook in TypeScript

Arrays can usually be used in useState hooks while getting data from an API. The following code segment demonstrates this while fetching data and displaying it using useState hooks.

interface IPost {
    userId : number ;
    id : number ;
    title  : string ;
    body : string ;
}

export default App = () => {

    const [ state, setState ] = 
    React.useState<IPost[]>([]);

    const getPosts = async () => {

      const res : 
      Response = await fetch(
     "https://jsonplaceholder.typicode.com/posts");
      const posts : IPost[] = await res.json();
      setState(posts);
    }

    React.useEffect(() => {
        getPosts();
    }, []);

    return (
        <>
            {
              state.map( (post,index) => 
              <div key={index}>
                    <h2>{post.title}</h2>
                    <div>{post.id}</div>
                    <p>{post.body}</p>
                </div>
              )
            }
        </>
    )
}Code language: JavaScript (javascript)

Conclusion

This is how we implement React hook useState in TypeScript. So, TypeScript can cleverly infer the type useState in many cases which is great. When TypeScript can’t infer the type we can pass it in as the generic parameter.

Have a nice day!

Sharing is caring

Did you like what Krishnaditya Biswas wrote? Thank them for their work by sharing it on social media.

0/10000

No comments so far