react js-tutorial

React useState Hook

useState is a Hook that allows you to have state variables in functional components. You pass the initial state to this function and it returns a variable with the current state value (not necessarily the initial state) and another function to update this value.

Class and functional components in React

There are two types of components in React: class and functional components.

Class components are ES6 classes that extend from React.Component and can have state and lifecycle methods:

class Message extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      message: ‘’    
    };
  }

  componentDidMount() {
    /* ... */
  }

  render() {
    return <div>{this.state.message}</div>;
  }
}

Functional components are functions that just accept arguments as the properties of the component and return valid JSX:

function Message(props) {
  return <div>{props.message}</div>
}
// Or as an arrow function
const Message = (props) =>  <div>{props.message}</div>

As you can see, there are no state or lifecycle methods. However, as of React 16.8, we can use Hooks.

React Hooks are functions that add state variables to functional components and instrument the lifecycle methods of classes. They tend to start with use.

What does the React.useState Hook do?

As stated previously, useState enables you to add state to function components. Calling React.useState inside a function component generates a single piece of state associated with that component.

Whereas the state in a class is always an object, with Hooks, the state can be any type. Each piece of state holds a single value, which can be an object, an array, a boolean, or any other type you can imagine.

So when should you use the useState Hook? It’s especially useful for local component state, but larger projects might require additional state management solutions.

Declaring state in React

useState is a named export from react. To use it, you can write:

React.useState

Or to import it just write useState:

import React, { useState } from 'react';

But unlike the state object that you can declare in a class, which allows you to declare more than one state variable, like this:

one state variable, like this:

import React from 'react';
class Message extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      message: '',
      list: [],    
    };
  }
  /* ... */
}

The useState Hook allows you to declare only one state variable (of any type) at a time, like this:

import React, { useState } from 'react';

const Message= () => {
   const messageState = useState( '' );
   const listState = useState( [] );
}

useState takes the initial value of the state variable as an argument.

You can pass it directly, as shown in the previous example, or use a function to lazily initialize the variable (useful when the initial state is the result of an expensive computation):

const Message= () => {
   const messageState = useState( () => expensiveComputation() );
   /* ... */
}

The initial value will be assigned only on the initial render (if it’s a function, it will be executed only on the initial render).

In subsequent renders (due to a change of state in the component or a parent component), the argument of the useState Hook will be ignored and the current value will be retrieved.

It is important to keep this in mind because, for example, if you want to update the state based on the new properties the component receives:

const Message= (props) => {
   const messageState = useState( props.message );
   /* ... */
}

Using useState alone won’t work because its argument is used the first time only — not every time the property changes (look here for the right way to do this).

But useState doesn’t return just a variable as the previous examples imply.

It returns an array, where the first element is the state variable and the second element is a function to update the value of the variable:

const Message= () => {
   const messageState = useState( '' );
   const message = messageState[0]; // Contains ''
   const setMessage = messageState[1]; // It’s a function
}

Usually, you’ll use array destructuring to simplify the code shown above:

const Message= () => {
   const [message, setMessage]= useState( '' );
}

This way, you can use the state variable in the functional component like any other variable:

const Message = () => {
  const [message, setMessage] = useState( '' );

  return (
    <p>
      <strong>{message}</strong>
    </p>
  );
};

But why does useState return array?

Because compared to an object, an array is more flexible and easy to use. If the method returned an object with a fixed set of properties, you wouldn’t be able to assign custom names in an easy way.

You’d have to do something like this (assuming the properties of the object are state and setState):

// Without using object destructuring
const messageState = useState( '' );
const message = messageState.state;
const setMessage = messageState

// Using object destructuring
const { state: message, setState: setMessage } = useState( '' );
const { state: list, setState: setList } = useState( [] );

RECOMMENDED ARTICLES





Leave a Reply

Your email address will not be published.