DevNotes

DevNotes

@devnotes ·

React Part 4: Remember things with state

Props come from outside. State is a component's own memory: what's typed in a box, whether a menu is open, what's in the cart. When state changes, React draws the component again. React's word for drawing is rendering, and you'll see it in its docs and error messages.

Why a normal variable doesn't work

JSX
function Counter() {  let count = 0;  return <button onClick={() => count++}>Clicked {count} times</button>;}

Click it and nothing changes. count does go up, but React has no idea it should draw again. And even if it did, the function would run from the top and set count back to 0.

useState

JSX
import { useState } from "react";
function Counter() {  const [count, setCount] = useState(0);
  return (    <button onClick={() => setCount(count + 1)}>      Clicked {count} times    </button>  );}

useState(0) gives you two things: the current value, starting at 0, and a function to change it. Calling setCount does two jobs. It stores the new value, and it tells React to run Counter again. On that run, useState hands back the new value, so the button shows it.

Functions whose names start with use are called hooks. Call them at the top of your component, never inside an if or a loop. React keeps track of each hook by the order it's called in, so the order has to be the same on every run.

Updates aren't instant

JSX
function handleClick() {  setCount(count + 1);  console.log(count); // still the old value}

The new value arrives on the component's next run, not on the next line. When the new state depends on the old one, especially if you update it more than once in a row, pass a function instead:

JSX
setCount((c) => c + 1);

React calls that function with the latest value, so three of these in a row really do add three.

Objects and arrays: replace them, don't change them

React notices a change when you give it a new value. If you change an object or array in place, it's still the same object, and React may not draw again:

JSX
const [cart, setCart] = useState([]);
cart.push(item);           // wrong: same array, no redrawsetCart([...cart, item]);  // right: a new array

Objects work the same way:

JSX
const [user, setUser] = useState({ name: "Asha", city: "Pune" });
setUser({ ...user, city: "Mumbai" });

Spread from JavaScript Part 7 makes this easy. A quick guide for arrays:

  • Add: [...list, item]
  • Remove: list.filter((x) => x.id !== id)
  • Update one: list.map((x) => (x.id === id ? { ...x, done: true } : x))

Every copy has its own state

Put two <Counter /> components on a page and each one keeps its own count. State belongs to each copy of a component on the screen, not to the function itself.

A menu that opens and closes

JSX
function Menu() {  const [open, setOpen] = useState(false);
  return (    <nav>      <button onClick={() => setOpen(!open)}>        {open ? "Close" : "Menu"}      </button>      {open && (        <ul>          <li>Home</li>          <li>Courses</li>        </ul>      )}    </nav>  );}

Try it

Build a quantity picker: a minus button, the number, and a plus button. Keep it between 1 and 10, and disable each button when it can't go any further, with disabled={qty === 1} on the minus.


Next: React Part 5: Events and forms

reactjavascriptwebdevbeginners

Join WIK to like and reply to DevNotes and see more like this.