February 3, 2022

React useState Hook With Examples

The React useState hook is used to add state to your functional component. That way you can track the state in a component.

What does useState hook do

When we say useState tracks the state in a functional component it means it can track the changes in the data in your component which may be any variable, object in the component. Any change in the data can be termed as the state change and you would want that change to be reflected in the DOM. That’s where you will require useState hook in React.

React useState hook example

Let’s first see a simple example where useState is not used. Here we have a component with one variable name and that name is rendered along with a button. Click of the button triggers the clickHandler() function where name is changed.

const MyApp = () => {
  let name = 'ReactUser';
  const clickHandler = () =>{
    name = 'NewReactUser';
    console.log('Updated Name', name)
  }
  return (
    <div>
      <p>My Name is {name}</p>
      <button onClick={clickHandler}>Change Name</button>
    </div>
  );
}

export default MyApp;

On adding this component in App.js and then on the browser on the click of the button you’d expect name to change.

import MyApp from "./Components/StateDemo/MyApp";

function App() {
  
  return (
    <MyApp></MyApp>
  );
}

export default App;
React useState

As evident from the console log, on clicking the button function is called but that change is not reflecting in the DOM thus not in the browser.

React evaluates the JXS in the component (and in child components) initially to translate into DOM instructions. After that React doesn't do the re-evaluation even if there is a change in the component (As shown in the above example). You will have to use state to let the React know that there is a state change and it should re-evaluate the component.

Let’s write the same React example again by introducing useState hook.

import { useState } from "react";
const MyApp = () => {
  //let name = 'ReactUser';
  const [name, setName] = useState('ReactUser');
  const clickHandler = () =>{
    setName('NewReactUser');
    //name = 'NewReactUser';
    console.log('Updated Name', name)
  }
  return (
    <div>
      <p>My Name is {name}</p>
      <button onClick={clickHandler}>Change Name</button>
    </div>
  );
}

export default MyApp;

The changes in the code are as following.

  1. You have to import the useState hook from react.
    import { useState } from "react";
    
  2. You have to initialize state by calling useState with in your component. useState hook accepts a single argument which is the initial state.
    useState('ReactUser');
    
  3. useState returns an array containing two values the current state and a function that updates it. Using array destructuring these values are assigned to name and setName.
    const [name, setName] = useState('ReactUser');
    
  4. With this declaration const [name, setName] = useState('ReactUser'); you are telling React that name is a "state variable" that can preserve values between the function calls and value can be changed using setName() function as done in the clickHandler() function.
  5. Any change to the state variable using the setter function indicates a state change prompting React to re-render the component.

Change state using previous state

In the above example name is updated whenever button is clicked. Since state variable preserve values between the function calls so using useState hook you can also update state value using the previous value. In this example we have a count variable that is incremented every time button is clicked.

import { useState } from "react";
const MyApp = () => {
  const [count, setCount] = useState(0);
  const clickHandler = () =>{
    setCount(count + 1);
  }
  return (
    <div>
      <p>You clicked button {count} times</p>
      <button onClick={clickHandler}>Click to increment</button>
    </div>
  );
}
export default MyApp;

Here count is state variable and setCount is the function to change value of the state variable. Initial state is 0. On every click of the button value of count is incremented by adding one to the previous state.

Using useState with forms

While working with forms you will use useState extensively to store values in order to retain values of form elements during component re-rendering.

As an example, we’ll create a ProductForm component where we’ll have input fields to enter product name and product price. The component just logs the values when the form is submitted.

Form example with multiple state hooks

You can create multiple state hooks to track individual form elements or create a single state to track an object. We’ll see how to do it both ways, first with multiple state hooks.

import { useState } from 'react';
const ProductForm = (props) => {
  const [pName, setEnteredName] = useState('');
  const [pPrice, setEnteredPrice] = useState('');

  const handleNameChange = (event) => {
    setEnteredName(event.target.value);
  }
  const handlePriceChange = (event) => {
    setEnteredPrice(event.target.value);
  }
  // Form submission handling
  const submitHandler = (event) => {
    event.preventDefault();
    // creating productItemData object 
    const productItemData = {
      name: pName,
      price: pPrice
    }
    console.log(productItemData);
    setEnteredName('');
    setEnteredPrice('');
  }

  return <form onSubmit={submitHandler}>
  <div>
    <h2>Product Form</h2>
    <div>
      <label>Product Name</label>
      <input type='text' name='name' value={pName} onChange={handleNameChange} />                
    </div>
    <div>
      <label>Price</label>
      <input type='number' name='price' value={pPrice} min="1" step="0.1" onChange={handlePriceChange}/>                
    </div>
  </div>
  <div>
    <button type='submit'>Add Product</button>          
  </div>
</form>
}
export default ProductForm;

Points to note here are-

  1. Two state hooks are created here to track product name and price.
        const [pName, setEnteredName] = useState('');
        const [pPrice, setEnteredPrice] = useState('');
    
    Initial value for the elements is just empty string. pName and pPrice are the state variables to preserve the values. s etEnteredName and setEnteredPrice are the functions to update the name and price values.
  2. In the input fields onChange event listener is used to wait for any value change in the corresponding fields. Whenever there is a change, handleNameChange or handlePriceChange is triggered which has the event object as a parameter.
  3. In these handle methods the mapped setter function is called to update the state variables with the current value of the input field.
  4. In the input fields value property is bound to the state so that whenever state changes input is also changed.
    value={pName}
    value={pPrice}
    

    That helps in clearing the fields once the form is submitted by setting the state to empty string. You can see the following lines in submitHandler() function.

    setEnteredName('');
    setEnteredPrice('');
    
  5. When the form is submitted submitHandler() function is called which creates an object using the entered field and logs it.
    <form onSubmit={submitHandler}>
    
React useState() in forms

Form example with Single state hook

Rather than having separate state hooks for individual form fields you can create a single state where you can pass an object.

    const [formField, setState] = useState({
        name: '',
        price: ''
    });

Here is the updated ProductForm code with the changes to use single state hook.

import { useState } from 'react';

const ProductForm = (props) => {
    const [formField, setState] = useState({
        name: '',
        price: ''
    });
    const handleInputChange = (event) => {
        const target = event.target;
        const value = target.value;
        const name = target.name;
    
        setState((prevState) => {
            return {...prevState, [name]: value};
        });
    }
    // Form submission handling
    const submitHandler = (event) => {
        event.preventDefault();
        const productItemData = {
            name:formField.name,
            price:formField.price
        }
        console.log(productItemData);
    }

    return <form onSubmit={submitHandler}>
    <div>
        <h2>Product Form</h2>
        <div>
            <label>Product Name</label>
            <input type='text' name='name' value={formField.name} onChange={handleInputChange} />                
        </div>
        <div>
            <label>Price</label>
            <input type='number' name='price' value={formField.price} min="1" step="1" onChange={handleInputChange}/>                
        </div>
    </div>
    <div>
        <button type='submit'>Add Product</button>          
    </div>
</form>
}

export default ProductForm;

Points to note here are-

  1. Single state is used now with initial state as empty string for object fields.
  2. In input fields onChange() listener is mapped to a single function handleInputChange() for all the fields. When calling setState() to update state spread operator is used to spread the previous state so that you can overwrite the individual fields without losing the data for other fields.
  3. Note that name attribute is also added in the input fields.
    name='name'
    name='price'
    

    That makes it convenient to use computed property name syntax to update the state key corresponding to the given input name (extracted using target.name).

    setState((prevState) => {
      return {...prevState, [name]: value};      
    });
    

That's all for the topic React useState Hook With Examples. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment