Bileşenler Arasında State Paylaşımı
Bazen, iki bileşenin state’inin her zaman birlikte değişmesini istersiniz. Bunu yapmak için, her iki bileşenden state’i kaldırın, en yakın ortak üst elemana taşıyın ve ardından onlara proplar aracılığıyla iletin. Bu, state’i yukarı kaldırma olarak bilinir ve React kodu yazarken yapacağınız en yaygın şeylerden biridir.
Bunları öğreneceksiniz
- Yukarı kaldırarak bileşenler arasında state paylaşımı nasıl yapılır?
- Kontrollü ve kontrolsüz bileşenler nedir?
Örnek ile state’in yukarı kaldırılması
Bu örnekte, bir üst Accordion
bileşeni iki ayrı Panel
bileşenini render eder:
Accordion
Panel
Panel
Her Panel
bileşeninin içeriğinin görünürlüğünü belirleyen bir boolean isActive
state’i vardır.
Her iki panel için de Göster düğmesine basın:
import { useState } from 'react'; function Panel({ title, children }) { const [isActive, setIsActive] = useState(false); return ( <section className="panel"> <h3>{title}</h3> {isActive ? ( <p>{children}</p> ) : ( <button onClick={() => setIsActive(true)}> Göster </button> )} </section> ); } export default function Accordion() { return ( <> <h2>Ankara, Türkiye</h2> <Panel title="Hakkında"> Ankara, Türkiye'nin başkenti ve İstanbul'dan sonra en kalabalık ikinci ilidir. </Panel> <Panel title="Etimoloji"> Belgelere dayanmayan ve günümüze kadar gelen söylentilere göre tarihte bahsedilen ilk adı Galatlar tarafından verilen ve Yunanca "çapa" anlamına gelen <i lang="el">Ankyra</i>'dır. Bu isim zamanla değişerek Ancyre, Engüriye, Engürü, Angara, Angora ve nihayet Ankara olmuştur. </Panel> </> ); }
Dikkat edin, bir panelin düğmesine basmak diğer paneli etkilemez. Bağımsızdırlar.
Ancak şimdi sadece bir panelin herhangi bir anda genişletilmesini istediğinizi varsayalım. Bu tasarımla, ikinci paneli genişletmek, birincisini daraltmalıdır. Bunu nasıl yapardın?
Bu iki paneli koordine etmek için, üç adımda “state’in yukarı kaldırılması” gerekiyor:
- Alt elemandan state’i kaldırın.
- Ortak üst elemandan hardcoded veriyi iletin.
- Ortak üst elemana state ekleyin ve olay işleyicileriyle birlikte aşağıya geçirin.
Bu Accordion
bileşeninin her iki Panel
’i koordine etmesine ve her seferinde yalnızca birini genişletmesine izin verecektir.
Adım 1: Alt elemandan state’i kaldırın.
Panel
’in isActive
kontrolünü üst elemanına vereceksiniz. Bu, üst elemanı isActive
’i Panel
’e prop olarak geçireceği anlamına gelir. Panel
bileşeninden bu satırı kaldırarak başlayın:
const [isActive, setIsActive] = useState(false);
Bunun yerine, isActive
’i Panel
’in prop listesine ekleyin:
function Panel({ title, children, isActive }) {
Şimdi, Panel
’in üst bileşeni isActive
’i bileşenlere prop’ları aktarma yöntemiyle kontrol edebilir. Tersine, Panel
bileşeninin artık isActive
’in değerini kontrol etme yetkisi yoktur — bu, artık üst bileşene bağlıdır!
Adım 2: Ortak üst elemandan hardcoded veriyi iletin.
State’i yukarı taşımak için, koordine etmek istediğiniz her iki alt bileşenin en yakın ortak üst bileşenini bulmanız gerekir:
Accordion
(en yakın üst eleman)Panel
Panel
Bu örnekte, Accordion
bileşenidir. Her iki panelin üzerinde ve prop’larını kontrol edebildiği için, o anda aktif olan panelin “gerçeklik kaynağı” olacaktır. Accordion
bileşeninin her iki panele de isActive
’in hardcoded bir değerini (örneğin true
) geçirmesini sağlayın:
import { useState } from 'react'; export default function Accordion() { return ( <> <h2>Ankara, Türkiye</h2> <Panel title="Hakkında" isActive={true}> Ankara, Türkiye'nin başkenti ve İstanbul'dan sonra en kalabalık ikinci ilidir. </Panel> <Panel title="Etimoloji" isActive={true}> Belgelere dayanmayan ve günümüze kadar gelen söylentilere göre tarihte bahsedilen ilk adı Galatlar tarafından verilen ve Yunanca "çapa" anlamına gelen <i lang="el">Ankyra</i>'dır. Bu isim zamanla değişerek Ancyre, Engüriye, Engürü, Angara, Angora ve nihayet Ankara olmuştur. </Panel> </> ); } function Panel({ title, children, isActive }) { return ( <section className="panel"> <h3>{title}</h3> {isActive ? ( <p>{children}</p> ) : ( <button onClick={() => setIsActive(true)}> Göster </button> )} </section> ); }
Accordion
bileşenindeki hardcoded isActive
değerlerini düzenlemeyi deneyin ve sonucu ekranda görün.
Adım 3: Ortak üst elemana state ekleyin.
State’i yukarı taşımak genellikle depoladığınız state’in doğasını değiştirir.
Bu durumda, aynı anda yalnızca bir panel aktif olmalıdır. Bu, Accordion
ortak üst bileşeninin hangi panelin aktif olduğunu takip etmesi gerektiği anlamına gelir. Bir boolean
değeri yerine, aktif Panel
’in state değişkeni için bir sayı kullanabilir:
const [activeIndex, setActiveIndex] = useState(0);
activeIndex
0
olduğunda, ilk panel aktiftir ve 1
olduğunda ikincisi aktif olur.
Herhangi Panel
içindeki “Göster” düğmesine tıklamak, Accordion
içindeki aktif indexi değiştirmelidir. Panel
doğrudan activeIndex
state’ini ayarlayamaz çünkü Accordion
içinde tanımlanmıştır. Accordion
bileşenin Panel
bileşeninin state’ini değiştirmesine izin vermesi için bir olay yöneticisini prop olarak aşağıya geçirmesi gerekir:
<>
<Panel
isActive={activeIndex === 0}
onShow={() => setActiveIndex(0)}
>
...
</Panel>
<Panel
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
...
</Panel>
</>
Panel
içindeki <button>
artık tıklama olayı yöneticisi olarak onShow
prop’unu kullanacaktır:
import { useState } from 'react'; export default function Accordion() { const [activeIndex, setActiveIndex] = useState(0); return ( <> <h2>Ankara, Türkiye</h2> <Panel title="Hakkında" isActive={activeIndex === 0} onShow={() => setActiveIndex(0)} > Ankara, Türkiye'nin başkenti ve İstanbul'dan sonra en kalabalık ikinci ilidir. </Panel> <Panel title="Etimoloji" isActive={activeIndex === 1} onShow={() => setActiveIndex(1)} > Belgelere dayanmayan ve günümüze kadar gelen söylentilere göre tarihte bahsedilen ilk adı Galatlar tarafından verilen ve Yunanca "çapa" anlamına gelen <i lang="el">Ankyra</i>'dır. Bu isim zamanla değişerek Ancyre, Engüriye, Engürü, Angara, Angora ve nihayet Ankara olmuştur. </Panel> </> ); } function Panel({ title, children, isActive, onShow }) { return ( <section className="panel"> <h3>{title}</h3> {isActive ? ( <p>{children}</p> ) : ( <button onClick={onShow}> Göster </button> )} </section> ); }
Bu state’i yukarı taşıma işlemini tamamlar! State’i ortak üst bileşene taşımak, iki paneli koordine etmenizi sağladı. “isShown” bayrakları yerine aktif index kullanmak, aynı anda yalnızca bir panelin aktif olmasını sağladı. Ve olay yöneticisini alt bileşene geçirmek, alt bileşenin üst bileşenin state’ini değiştirmesine izin verdi.
Derinlemesine İnceleme
It is common to call a component with some local state “uncontrolled”. For example, the original Panel
component with an isActive
state variable is uncontrolled because its parent cannot influence whether the panel is active or not.
In contrast, you might say a component is “controlled” when the important information in it is driven by props rather than its own local state. This lets the parent component fully specify its behavior. The final Panel
component with the isActive
prop is controlled by the Accordion
component.
Uncontrolled components are easier to use within their parents because they require less configuration. But they’re less flexible when you want to coordinate them together. Controlled components are maximally flexible, but they require the parent components to fully configure them with props.
In practice, “controlled” and “uncontrolled” aren’t strict technical terms—each component usually has some mix of both local state and props. However, this is a useful way to talk about how components are designed and what capabilities they offer.
When writing a component, consider which information in it should be controlled (via props), and which information should be uncontrolled (via state). But you can always change your mind and refactor later.
A single source of truth for each state
In a React application, many components will have their own state. Some state may “live” close to the leaf components (components at the bottom of the tree) like inputs. Other state may “live” closer to the top of the app. For example, even client-side routing libraries are usually implemented by storing the current route in the React state, and passing it down by props!
For each unique piece of state, you will choose the component that “owns” it. This principle is also known as having a “single source of truth”. It doesn’t mean that all state lives in one place—but that for each piece of state, there is a specific component that holds that piece of information. Instead of duplicating shared state between components, lift it up to their common shared parent, and pass it down to the children that need it.
Your app will change as you work on it. It is common that you will move state down or back up while you’re still figuring out where each piece of the state “lives”. This is all part of the process!
To see what this feels like in practice with a few more components, read Thinking in React.
Özet
- When you want to coordinate two components, move their state to their common parent.
- Then pass the information down through props from their common parent.
- Finally, pass the event handlers down so that the children can change the parent’s state.
- It’s useful to consider components as “controlled” (driven by props) or “uncontrolled” (driven by state).
Problem 1 / 2: Synced inputs
These two inputs are independent. Make them stay in sync: editing one input should update the other input with the same text, and vice versa.
import { useState } from 'react'; export default function SyncedInputs() { return ( <> <Input label="First input" /> <Input label="Second input" /> </> ); } function Input({ label }) { const [text, setText] = useState(''); function handleChange(e) { setText(e.target.value); } return ( <label> {label} {' '} <input value={text} onChange={handleChange} /> </label> ); }