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.