State

What state is, how to define it with useState, how to update it, updating based on the previous state, and state immutability -- with simple examples.

Beginner 12 min
TR

State

In Events, we learned how to catch a user's actions, but our handlers only ever logged to console.log -- nothing changed on screen. This lesson covers how to actually change the screen in React: state.

What Is State?

State is data a component "remembers" that can change over time. When state changes, React automatically re-renders that component -- it redraws the screen to match the latest state. A counter, an input's current value, whether a menu is open or closed -- all of these are examples of state.

Defining State with useState

You define state with the useState function:

import { useState } from "react";

function UseStateBasicExample() {
  // useState(0), state'i 0 başlangıç değeriyle kurar.
  // count: mevcut değeri okumak için. setCount: değeri güncellemek için.
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Sayaç: {count}</p>
      <button onClick={() => setCount(count + 1)}>Artır</button>
    </div>
  );
}

useState(0) sets up state with an initial value of 0 and returns two things: the current value (count) and a function to update it (setCount). The const [count, setCount] = ... syntax is a JavaScript feature (array destructuring) that splits these two values into separate variables on one line -- similar to the object destructuring we saw in Props, but for arrays.

Updating State

You can't update state by writing count = count + 1 directly -- you have to call the function useState gave you (setCount):

import { useState } from "react";

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

  function increment() {
    setCount(count + 1);
  }

  function reset() {
    setCount(0);
  }

  return (
    <div>
      <p>Sayaç: {count}</p>
      <button onClick={increment}>+1</button>
      <button onClick={reset}>Sıfırla</button>
    </div>
  );
}

When setCount(...) is called, React does two things: it saves the new value of the state, and it re-renders the component with that new value. Directly assigning to a variable doesn't tell React "something changed," so it doesn't update the screen -- we'll see this in State vs. a Regular Variable.

Updating Based on the Previous State

If calculating the new state needs the previous state, you should pass setCount a FUNCTION instead of a direct value:

import { useState } from "react";

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

  function incrementTwice() {
    // YANLIŞ: count burada hâlâ ESKİ değeri gösterir, iki satır da aynı
    // "eski değer + 1"i kullanır -- sonuçta yalnızca 1 artar, 2 değil.
    // setCount(count + 1);
    // setCount(count + 1);

    // DOĞRU: fonksiyon formu (prevCount => ...), her seferinde bir önceki
    // güncellemeden sonraki en güncel değeri kullanır.
    setCount((prevCount) => prevCount + 1);
    setCount((prevCount) => prevCount + 1);
  }

  return (
    <div>
      <p>Sayaç: {count}</p>
      <button onClick={incrementTwice}>2 Artır</button>
    </div>
  );
}

Writing setCount(count + 1) isn't reliable if it's called more than once within the same render -- the count variable stays fixed for that entire render. Writing setCount((prevCount) => prevCount + 1) tells React "whatever happens, calculate based on the MOST RECENT value."

State vs. a Regular Variable

Why not just use a regular variable with let? Because changing a regular variable doesn't update the screen:

function StateVsVariableExample() {
  let plainCount = 0; // Normal bir değişken -- state DEĞİL.

  function handleClick() {
    plainCount = plainCount + 1;
    console.log("plainCount şimdi:", plainCount);
    // Değer aslında değişti, ama ekranda hiçbir şey güncellenmez --
    // çünkü React, normal bir değişkenin değiştiğini bilmiyor.
  }

  return (
    <div>
      <p>Ekrandaki değer hep aynı kalır: {plainCount}</p>
      <button onClick={handleClick}>Artır (Ekran Değişmez)</button>
    </div>
  );
}

plainCount really does change (you can see it in the console), but since React doesn't know about it, it doesn't re-render the component -- the screen keeps showing the same value. useState is special because it tells React "let me know when this value changes."

State Immutability

If state is an object or array, instead of directly changing (mutating) it, you should always create a NEW object/array and pass that to the setter function:

import { useState } from "react";

function StateImmutabilityExample() {
  const [user, setUser] = useState({ name: "Ayşe", age: 25 });

  function haveBirthday() {
    // YANLIŞ: mevcut nesneyi doğrudan değiştirmek (mutate etmek).
    // React bu değişikliği fark etmeyebilir, ekran güncellenmeyebilir.
    // user.age = user.age + 1;
    // setUser(user);

    // DOĞRU: spread (...) ile eski değerleri kopyalayan YENİ bir nesne
    // oluşturup onu state'e veriyoruz.
    setUser({ ...user, age: user.age + 1 });
  }

  return (
    <div>
      <p>
        {user.name}, {user.age} yaşında.
      </p>
      <button onClick={haveBirthday}>Doğum Günü</button>
    </div>
  );
}

Writing user.age = user.age + 1 and passing back the same object doesn't guarantee React notices the change -- React decides whether to re-render partly by checking whether the state is "the same object or a different one." The spread operator ({ ...user, age: ... }) copies all the fields of the old object into a new one, changing only the field you specify.

Summary and Glossary

State is a component's data that can change over time and, when it changes, automatically updates the screen. It's defined with useState and can only be updated through its setter function -- direct assignment doesn't work. When updating based on the previous state, use the function form. Object/array state is never mutated directly -- a new copy is always created.

Glossary

State — A component's data that can change over time and, when it changes, triggers the component to re-render.

useState — The React function that lets you add state to a component; it returns the current value and a function to update it.

Re-render — React re-running a component and updating the screen when its state or props change.

Immutability — The principle of creating a new copy that includes the change, instead of directly modifying an existing object/array.