React - useState Hook

In the article: React Hooks I described what Hooks are and why they are so great. Now we will take a closer look at the Hooks. In this article we will discuss: useState. With useState we get a simple function to use one or more states in a Functional Component.

Let's first have a look at how a state looks like in a class component:

import React from "react";

export default class SomeClass extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      firstName: "Jane",
      lastName: "Doe",
      age: 32,
    };
  }

  render() {
    return (
      <div>
        <p>
          My Name is {this.state.firstName} {this.state.lastName} and I'm{" "}
          {this.state.age} years old.
        </p>
      </div>
    );
  }
}

CodeSandbox

In this simple example, the text in the render function is completed with values from the state. These values are set initially in the state. The initial values can also be set by props. This looks as follows:

import React from "react";

class SomeClass extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      firstName: props.firstName,
      lastName: props.lastName,
      age: props.age,
    };
  }

  render() {
    return (
      <div>
        <p>
          My Name is {this.state.firstName} {this.state.lastName}
          and I'm {this.state.age} years old.
        </p>
      </div>
    );
  }
}

export default class OtherClass extends React.Component {
  render() {
    return (
      <div>
        <SomeClass firstName="Jane" lastName="Doe" age={32} />
      </div>
    );
  }
}

CodeSandbox

This raises the question, why we don't use the props directly in the render function? This is quite simple, then we could change the values only by the component above. So the props serve only as initial values. Now we want to use buttons to change the age of our person.

import React from "react";

class SomeClass extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      firstName: props.firstName,
      lastName: props.lastName,
      age: props.age,
    };
  }

  render() {
    return (
      <div>
        <p>
          My Name is {this.state.firstName} {this.state.lastName}
          and I'm {this.state.age} years old.
        </p>
        <button onClick={() => this.setState({ age: this.state.age - 1 })}>
          -1 year
        </button>
        <button onClick={() => this.setState({ age: this.state.age + 1 })}>
          +1 year
        </button>
      </div>
    );
  }
}

export default class OtherClass extends React.Component {
  render() {
    return (
      <div>
        <SomeClass firstName="Jane" lastName="Doe" age={32} />
      </div>
    );
  }
}

CodeSandbox

With the buttons it is now possible to increase or decrease the age of our person by one year. But how does such a Class Component look like as a Functional Component with the useState Hook?

import React, { useState } from "react";

export default function SomeFunction() {
  const [firstName, setFirstName] = useState("Jane");
  const [lastName, setLastName] = useState("Doe");
  const [age, setAge] = useState(32);

  return (
    <div>
      <p>
        My Name is {firstName} {lastName}
        and I'm {age} years old.
      </p>
      <button onClick={() => setAge(age - 1)}>-1 year</button>
      <button onClick={() => setAge(age + 1)}>+1 year</button>
    </div>
  );
}

CodeSandbox

As we can see we can write cleaner code in the return but have 3 times the useState Hook. In this short example, this might be ok but if we build a bigger state the hook useReducer is the better choice. We will come to this in another article. Of course, we can also use the props as inital Values with useState, which looks like this:

import React, { useState } from "react";

function SomeFunction(props) {
  const [firstName, setFirstName] = useState(props.firstName);
  const [lastName, setLastName] = useState(props.lastName);
  const [age, setAge] = useState(props.age);

  return (
    <div>
      <p>
        My Name is {firstName} {lastName}
        and I'm {age} years old.
      </p>
      <button onClick={() => setAge(age - 1)}>-1 year</button>
      <button onClick={() => setAge(age + 1)}>+1 year</button>
    </div>
  );
}

export default function OtherFunction() {
  return (
    <div>
      <SomeFunction firstName="Jane" lastName="Doe" age={32} />
    </div>
  );
}

CodeSandbox

In the next article, we will deal with the hook useReducer and look at how we can simplify our example and need only one call of the useReducer hook instead of 3 calls of useState.