Skip to main content
State allows components to remember and react to user interactions. React’s useState hook is the foundation of state management.

The useState Hook

Import useState from React and use it to create state variables:
import { useState } from "react";

const [count, setCount] = useState(Number);
useState returns an array with two elements: the current state value and a function to update it.

Complete State Example

Here’s a counter component with state management:
import { useState } from "react";
import cesese from './ItemCounter.module.css';

interface Props {
  name: string;
  quantity?: number;
}

export const ItemCounter = ({ name, quantity }: Props) => {
  const [count, setCount] = useState(Number);

  const handleAdd = () => {
    setCount(count + 1);
  };

  const handleSub = () => {
    if (count === 0) return;
    setCount(count - 1);
  };

  const handleClick = () => {
    console.log(`homero homero homero ${name}`);
  };

  return (
    <section className={cesese['item-row']}>
      <span 
        className="item-text"
        style={{
          color: count === 0 ? 'red' : 'black',
        }}
      >
        {name}
      </span>
      <button
        onClick={(event) => {
          handleAdd();
        }}
      >
        +1
      </button>
      <span>{count}</span>
      <button onClick={handleSub}>-1</button>
    </section>
  );
};

How State Works

1

Initialize state

Call useState with an initial value (or type like Number).
const [count, setCount] = useState(Number);
2

Read state

Use the state variable directly in your JSX.
<span>{count}</span>
3

Update state

Call the setter function to update the state.
setCount(count + 1);
4

React re-renders

When state changes, React automatically re-renders the component with the new value.

Event Handlers

Create handler functions to manage state updates:
const handleAdd = () => {
  setCount(count + 1);
};

const handleSub = () => {
  if (count === 0) return;
  setCount(count - 1);
};
Add validation logic in your handlers (like if (count === 0) return;) to prevent invalid states.

Connecting Events to Handlers

There are two ways to connect event handlers:

Direct Reference

<button onClick={handleSub}>-1</button>

Inline Arrow Function

<button
  onClick={(event) => {
    handleAdd();
  }}
>
  +1
</button>
Use inline arrow functions when you need to pass parameters or call multiple functions.

Conditional Styling with State

Use state to dynamically change styles:
<span 
  className="item-text"
  style={{
    color: count === 0 ? 'red' : 'black',
  }}
>
  {name}
</span>
This changes the text color to red when the count reaches zero.

State Best Practices

1

Never mutate state directly

Always use the setter function. Don’t do: count = count + 1.
// ❌ Wrong
count = count + 1;

// ✅ Correct
setCount(count + 1);
2

Add validation

Prevent invalid state transitions with conditional checks.
if (count === 0) return;
setCount(count - 1);
3

Use meaningful names

Name your state variables and setters descriptively.
const [count, setCount] = useState(0);
const [isLoading, setIsLoading] = useState(false);
4

Keep state minimal

Only store data that changes and can’t be calculated from other state.

Debugging State

Log state values to understand how they change:
const handleClick = () => {
  console.log(`homero homero homero ${name}`);
};
Install React Developer Tools browser extension to inspect component state in real-time.

Common State Patterns

Counter Pattern

const [count, setCount] = useState(0);

const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);

Toggle Pattern

const [isOpen, setIsOpen] = useState(false);

const toggle = () => setIsOpen(!isOpen);

Input Pattern

const [text, setText] = useState("");

const handleChange = (e) => setText(e.target.value);

Build docs developers (and LLMs) love