Controlled Components

What a controlled component is, binding an input to state with value and onChange, using the same pattern for checkboxes/selects -- with simple examples.

Intermediate 11 min
TR

Controlled Components

In Events we saw onChange, and in State we saw useState, separately. This lesson brings the two together into the controlled component -- the fundamental pattern for writing forms in React.

What Is a Controlled Component?

Normally, an <input> keeps its own value inside itself (in the browser's DOM) -- React might not even be aware of it. In a controlled component, the input's value is DETERMINED by React's state instead: the input doesn't hold a value on its own, it always gets its value from state.

Binding an Input to State with value

The first step in making an input controlled is binding the value attribute to a state variable:

import { useState } from "react";

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

  function handleChange(event) {
    setText(event.target.value);
  }

  return (
    <div>
      {/* value={text}, input'un ekranda gösterdiği değeri React'in state'i
          BELİRLESİN diyor -- input artık kendi başına bir değer tutmuyor,
          değerini tamamen `text` state'inden alıyor. */}
      <input type="text" value={text} onChange={handleChange} />
      <p>Yazdığın: {text}</p>
    </div>
  );
}

Since we wrote value={text}, the value shown in the input is ALWAYS whatever text currently holds in state. But that alone isn't enough -- something also needs to happen when the user types, which we'll see in the next section.

Updating State with onChange

value alone makes the input read-only -- the user can't type anything. To keep state up to date, you also need onChange:

import { useState } from "react";

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

  // Değer state'te olduğu için, kullanıcı her harf yazdığında ekranın
  // BAŞKA bir yerinde de anında kullanabiliyoruz -- örneğin karakter
  // sayısını ya da büyük harfli önizlemesini göstermek gibi.
  return (
    <div>
      <input
        type="text"
        value={text}
        onChange={(event) => setText(event.target.value)}
        placeholder="Bir şey yaz..."
      />
      <p>Karakter sayısı: {text.length}</p>
      <p>Büyük harf önizleme: {text.toUpperCase()}</p>
    </div>
  );
}

Every keystroke runs onChange, setText updates state, React re-renders, and the input's value reflects that new state. This loop (type → update state → re-render → show in input) is the heart of a controlled component. Since the value lives in state, we can also use it instantly somewhere else on screen at the same time (a character count, a preview, etc.).

Controlled Checkboxes and Selects

The same pattern isn't limited to text inputs -- checkboxes and selects can be controlled too:

import { useState } from "react";

function ControlledCheckboxExample() {
  const [isSubscribed, setIsSubscribed] = useState(false);

  // Checkbox'larda value yerine checked kullanılır -- mantık aynı:
  // checkbox'ın işaretli olup olmadığını React'in state'i belirliyor.
  function handleChange(event) {
    setIsSubscribed(event.target.checked);
  }

  return (
    <div>
      <label>
        <input type="checkbox" checked={isSubscribed} onChange={handleChange} />
        Bültene abone ol
      </label>
      <p>{isSubscribed ? "Abonesin." : "Abone değilsin."}</p>
    </div>
  );
}

Checkboxes use checked instead of value, but the logic is the same: React's state decides whether it's checked.

import { useState } from "react";

function ControlledSelectExample() {
  const [language, setLanguage] = useState("tr");

  return (
    <div>
      {/* select'ler de aynı desenle kontrol edilir: value + onChange. */}
      <select value={language} onChange={(event) => setLanguage(event.target.value)}>
        <option value="tr">Türkçe</option>
        <option value="en">English</option>
      </select>
      <p>Seçilen dil: {language}</p>
    </div>
  );
}

A <select> is controlled with value and onChange too, just like a text input.

Why Controlled Components?

One of the most concrete benefits of keeping a value in React's state is that you can easily do things with it -- like resetting it programmatically:

import { useState } from "react";

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

  function handleClear() {
    // Değer state'te olduğu için, input'u temizlemek yalnızca state'i
    // sıfırlamak kadar basit -- gerçek DOM elemanına dokunmaya
    // (useRef ya da event.target.reset() gibi) hiç gerek yok.
    setText("");
  }

  return (
    <div>
      <input
        type="text"
        value={text}
        onChange={(event) => setText(event.target.value)}
      />
      <button onClick={handleClear}>Temizle</button>
    </div>
  );
}

Since the value lives in state, clearing the input is as simple as setText(""). If we kept the value outside React (in the browser's own DOM), we'd need a DOM reference (useRef) or the form's own reset() method to do the same thing.

Summary and Glossary

A controlled component is a form element bound to state with value (or checked for checkboxes) and kept up to date with onChange. Since the value always lives in React's state, reading, validating, or resetting it becomes easier -- you don't have to rely on a value the input holds on its own.

Glossary

Controlled Component — A form element whose value is determined by React's state and updated with onChange.

Uncontrolled Component — A form element that holds its own value (in the browser's DOM), not tied to React state.