State and Hooks

Learn how to manage component state using React hooks like useState and useEffect

State and Hooks

State allows React components to change their output over time in response to user actions, network responses, and anything else.

What is State?

State is data that can change over time. When state changes, React re-renders the component to reflect the new state.

useState Hook

The useState hook lets you add state to function components:

import { useState } from 'react';

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

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

useEffect Hook

The useEffect hook lets you perform side effects in function components:

import { useState, useEffect } from 'react';

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(seconds => seconds + 1);
    }, 1000);

    // Cleanup function
    return () => clearInterval(interval);
  }, []); // Empty dependency array means this effect runs once

  return <div>Timer: {seconds} seconds</div>;
}

Managing Complex State

For more complex state, you can use multiple useState calls or useReducer:

function UserProfile() {
  const [user, setUser] = useState({
    name: '',
    email: '',
    age: 0
  });

  const updateUser = (field, value) => {
    setUser(prevUser => ({
      ...prevUser,
      [field]: value
    }));
  };

  return (
    <form>
      <input
        type="text"
        value={user.name}
        onChange={(e) => updateUser('name', e.target.value)}
        placeholder="Name"
      />
      <input
        type="email"
        value={user.email}
        onChange={(e) => updateUser('email', e.target.value)}
        placeholder="Email"
      />
      <input
        type="number"
        value={user.age}
        onChange={(e) => updateUser('age', parseInt(e.target.value))}
        placeholder="Age"
      />
    </form>
  );
}

Effect Dependencies

The dependency array in useEffect determines when the effect runs:

// Runs on every render
useEffect(() => {
  console.log('Runs on every render');
});

// Runs only once (on mount)
useEffect(() => {
  console.log('Runs only once');
}, []);

// Runs when count changes
useEffect(() => {
  console.log('Count changed:', count);
}, [count]);

Common Patterns

Fetching Data

function UserList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch('/api/users')
      .then(response => response.json())
      .then(data => {
        setUsers(data);
        setLoading(false);
      });
  }, []);

  if (loading) return <div>Loading...</div>;

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

Rules of Hooks

  1. Only call hooks at the top level of your React function
  2. Only call hooks from React functions (components or custom hooks)
  3. Use the ESLint plugin for React hooks to catch violations

Exercise

Create a todo list component that allows users to add, remove, and toggle the completion status of todo items using useState.

Next up: We'll learn about handling events and forms in React!