Intro to React Hooks - useState

For stateful and side-effect functionality, React provides what are known as hooks. These are JavaScript functions typically imported from React, defined in your own files, or imported from external packages. Hooks must begin with use in order to distinguish from other JS functions.

Hooks can only be called inside your functional React components. "Calling a hook" just means running the function, typically with initial or default values. The hooks can return anything or nothing, there are no hard rules about what a hook is supposed to do- but there are rules about when and where you can call a hook.

The most common hook is useState. We will demonstrate this hook with a simple counter example, which is also used in React's useState docs. The useState hook takes in an initial state value. In our example, this is an intial count value.

useState returns an array- the first element in the array is the current value of the state. On the first render, the current state value is equal to the initial state value passed to the hook. The second value in the array is the setter. This allows you to set the new state value. In our example, this is setCount. The notation of const [count, setCount] is using destructuring to name the array values, since array values do not have variable names.

The setter (also known as setState) can accept the new current value, or a function. In our example, we demonstrate increment implementations with the new value, and function. The functional implementation should only be used if you need to compute the new state based on the previous state value. In most cases, you will have the current state value in scope, so you usually don't need a function.

An example of when you would need a function is if you need to perform multiple state updates in a row that depend on each other, and so in that case, using the current value of the state would not be up to date in the second setState call, because the first call hasn't caused a rerender yet.

Calling the setState will trigger a rerender on the component, but only if the value is different than the previous render.

setState object example

We also demonstrate an object example of terminating and hiring a user by setting a boolean value inside of an object. By using the object spread syntax, we call setUser with the current user spread ...user over a new object, and then isEmployed with the new boolean value determined based on the current boolean value. You cannot simply call setUser({isEmployed: true}) because this would set the entire new object and exclude the User's name. So, we use the spread syntax to insert all of the current user's keys and values into the new object, and then by adding the isEmployed key and value, we overwrite that value in the object (since the user would contain the old key / value). Because objects are built by insertion order, the last key / value of isEmployed would take precedence.

Menu
Lesson 22/22
import { useState } from "react";

const INITIAL_COUNT = 0;
const INITIAL_USER = { name: "Bob", isEmployed: true };

function CounterExample() {
  const [count, setCount] = useState(INITIAL_COUNT);
  return (
    <>
      <h1> Counter example </h1>
      <h3> Count: {count}</h3>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={() => setCount((prevCount) => prevCount + 1)}>
        Increment (with function)
      </button>
      <button onClick={() => setCount(count - 1)}>Decrement</button>
    </>
  );
}

function ObjectExample() {
  const [user, setUser] = useState(INITIAL_USER);
  const { name, isEmployed } = user;
  return (
    <>
      <h1> Object example </h1>
      <h3>
        {name} is {isEmployed ? "employed" : "not employed"}
      </h3>
      <button
        onClick={() =>
          setUser({ ...user, isEmployed: isEmployed ? false : true })
        }
      >
        {isEmployed ? "Terminate" : "Hire"}
      </button>
    </>
  );
}

export default function App() {
  return (
    <div>
      <CounterExample />
      <ObjectExample />
    </div>
  );
}