Sharing State
Şimdiye kadar useState'i hep TEK bir component'in içinde kullandık.
Bu ders, birden fazla component'in AYNI state'e ihtiyaç duyduğu
durumları -- ve bunun beraberinde getirdiği "props drilling" sorununu
-- anlatıyor.
İki Component, Aynı State'e İhtiyaç Duyduğunda
Bir arama kutusu (SearchBox) ve bir sonuç listesi (ResultsList)
düşün -- ikisi de AYNI arama metnine ihtiyaç duyar. query state'i
SearchBox'ın içinde tutulursa ne olur?
import { useState } from "react";
function SearchBox() {
const [query, setQuery] = useState("");
return (
<input
type="text"
value={query}
onChange={(event) => setQuery(event.target.value)}
placeholder="Search courses..."
/>
);
}
function ResultsList() {
// Sorun: bu component'in, kullanıcının SearchBox'a ne yazdığını bilmesi
// gerekiyor -- ama `query` state'i SearchBox'ın İÇİNDE hapsolmuş, buraya
// hiçbir şekilde ulaşamıyor.
return <p>Type in the search box above -- but this list has no way to know what you typed.</p>;
}
function SeparateStateProblemExample() {
return (
<div>
<SearchBox />
<ResultsList />
</div>
);
}
ResultsList'in, SearchBox'ın state'ine erişmesinin hiçbir yolu yok
-- her component'in kendi state'i kendi İÇİNE hapsolmuştur, kardeş
component'ler (sibling'ler) birbirinin state'ini DOĞRUDAN göremez.
State'i Yukarı Taşımak: Lifting State Up
Çözüm, state'i iki component'in de ORTAK atası olan bir yere taşımak:
import { useState } from "react";
const courses = ["Java", "React", "Spring Boot"];
function SearchBox({ query, onQueryChange }) {
return (
<input
type="text"
value={query}
onChange={(event) => onQueryChange(event.target.value)}
placeholder="Search courses..."
/>
);
}
function ResultsList({ query }) {
const filtered = courses.filter((course) =>
course.toLowerCase().includes(query.toLowerCase()),
);
return (
<ul>
{filtered.map((course) => (
<li key={course}>{course}</li>
))}
</ul>
);
}
function LiftingStateUpExample() {
// `query` state'i artık SearchBox'ın İÇİNDE değil, ikisinin de ORTAK
// atası olan bu component'te yaşıyor -- "state'i yukarı taşımak"
// (lifting state up) dediğimiz şey bu. Her iki child da bu state'i
// props ile alıyor.
const [query, setQuery] = useState("");
return (
<div>
<SearchBox query={query} onQueryChange={setQuery} />
<ResultsList query={query} />
</div>
);
}
query state'i artık ortak parent'ta yaşıyor; her iki child da bunu
props ile alıyor -- SearchBox, query ve onQueryChange'i, ResultsList
ise yalnızca query'i. Bu desene state'i yukarı taşımak (lifting
state up) denir -- React'te paylaşılan state'i yönetmenin en temel
yolu budur.
Aynı Deseni Farklı Bir Senaryoda Görmek
Lifting state up, yalnızca liste filtrelemekle sınırlı değil -- aynı değeri İKİ FARKLI şekilde gösteren component'ler için de geçerli:
import { useState } from "react";
function SliderInput({ rating, onRatingChange }) {
return (
<input
type="range"
min="0"
max="5"
value={rating}
onChange={(event) => onRatingChange(Number(event.target.value))}
/>
);
}
function RatingDisplay({ rating }) {
return <p>Rating: {rating} / 5</p>;
}
function SyncedSiblingsExample() {
// İki farklı görünüme (bir slider, bir metin) sahip iki kardeş
// component, AYNI değeri temsil ediyor -- state, ortak ataya
// taşındığı için ikisi de her zaman senkron kalıyor.
const [rating, setRating] = useState(0);
return (
<div>
<SliderInput rating={rating} onRatingChange={setRating} />
<RatingDisplay rating={rating} />
</div>
);
}
Bir slider ve bir metin gösterimi, AYNI rating değerini paylaşıyor --
state ortak parent'ta olduğu için, biri değiştiğinde diğeri de anında
güncel kalıyor.
Props Drilling: Ara Katmanlardan Geçirmek
Component ağacı derinleştikçe, bir prop'u ihtiyaç duyan component'e ULAŞTIRMAK için, aralarındaki component'lerin de o prop'u almasını gerektirebilir:
import { useState } from "react";
const courses = ["Java", "React", "Spring Boot"];
function SearchBox({ query, onQueryChange }) {
return (
<input
type="text"
value={query}
onChange={(event) => onQueryChange(event.target.value)}
placeholder="Search courses..."
/>
);
}
function ResultsList({ query }) {
const filtered = courses.filter((course) =>
course.toLowerCase().includes(query.toLowerCase()),
);
return (
<ul>
{filtered.map((course) => (
<li key={course}>{course}</li>
))}
</ul>
);
}
function ResultsPanel({ query }) {
// ResultsPanel, `query`'i KENDİSİ hiç kullanmıyor -- yalnızca
// ResultsList'e ULAŞTIRMAK için alıyor. Bu, "props drilling" (props'u
// zorunlu olarak ara katmanlardan geçirmek) dediğimiz durumun basit
// bir örneği.
return (
<div className="panel">
<ResultsList query={query} />
</div>
);
}
function PropsDrillingExample() {
const [query, setQuery] = useState("");
return (
<div>
<SearchBox query={query} onQueryChange={setQuery} />
<ResultsPanel query={query} />
</div>
);
}
ResultsPanel, query'i KENDİSİ hiç kullanmıyor -- yalnızca
ResultsList'e iletmek için alıyor. Buna props drilling denir --
bir prop'un, kullanmayan ara katmanlardan zorunlu olarak geçirilmesi.
Props Drilling Neden Sorun Yaratır?
Ağaç derinleştikçe bu sorun büyür:
function Level1({ user }) {
return <Level2 user={user} />;
}
function Level2({ user }) {
return <Level3 user={user} />;
}
function Level3({ user }) {
return <Level4 user={user} />;
}
function Level4({ user }) {
return <p>Logged in as {user}</p>;
}
function WhyPropsDrillingHurtsExample() {
// Level1, Level2, Level3'ün HİÇBİRİ `user`'ı kullanmıyor -- yalnızca bir
// sonraki seviyeye AKTARIYORLAR. Yalnızca en dipteki Level4 gerçekten
// kullanıyor. Ağaç derinleştikçe (ya da her seviyeye yeni prop'lar
// eklendikçe) bu, hem yazması yorucu hem de hataya açık bir hal alır --
// bir sonraki derste (Context API), bunu çözen bir yöntem göreceğiz.
return <Level1 user="Ada" />;
}
Level1, Level2, Level3'ün HİÇBİRİ user'ı kullanmıyor -- yalnızca
aktarıyorlar. Yalnızca en dipteki Level4 gerçekten kullanıyor. Her
yeni seviye, ya da her yeni paylaşılan değer, bu zinciri daha da
uzatır -- yazması yorucu ve hataya açık hale gelir. Bir sonraki derste
(Context API), bunu çözen bir yöntem göreceğiz.
Özet ve Terimler Sözlüğü
Birden fazla component aynı state'e ihtiyaç duyduğunda, state'i ORTAK ataya taşımak (lifting state up) gerekir -- her child bu state'i props ile alır. Ağaç derinleştikçe, bir prop'u kullanmayan ara component'lerden geçirmek zorunda kalmak "props drilling" sorununu doğurur -- bu, kodu yazması yorucu ve değiştirmesi kırılgan hale getirir.
Terimler Sözlüğü
Lifting State Up (State'i Yukarı Taşımak) — Birden fazla component'in paylaştığı bir state'i, bu component'lerin ORTAK atasına taşımak.
Props Drilling — Bir prop'u, kullanmayan ara katman component'lerden zorunlu olarak geçirmek.