Bağlam ile Derin Veri Aktarımı
Genelde, bir üst bileşenden bir alt bileşene bilgi geçişi yaparken props kullanırsınız. Ancak, çok sayıda bileşen arasında props geçmeniz gerektiğinde veya uygulamanızdaki birçok bileşen aynı bilgiye ihtiyaç duyduğunda, props geçişi fazlasıyla uzun ve rahatsız edici hale gelebilir. Bağlam (Context), üst bileşenin ağaçtaki herhangi bir bileşenin kullanımına kapalı olan bilgi sağladığından, onu açıkça prop olarak geçmeden yapmak mümkündür.
- "Prop delme" nedir
- Tekrarlı prop geçişinin bağlam ile nasıl değiştirileceği
- Bağlam için yaygın kullanım durumları
- Bağlama alternatif yaygın yöntemler
Props Geçmenin Problemi {/the-problem-with-passing-props/}
Props geçişi
, verileri UI ağacınızda onu kullanan bileşenlere açıkça iletmek için harika bir yoldur.
Ancak, bazı prop'ları ağaçta derinlemesine geçmeniz gerektiğinde veya birçok bileşenin aynı prop'a ihtiyaç duyduğunda, props geçişi rahatsız edici ve uzun hale gelebilir. En yakın ortak üst bileşen, verilere ihtiyaç duyan bileşenlerden uzak olabilir ve durumu yukarı çıkarmak
bu yüksekliğinde "prop delme" adı verilen bir duruma yol açabilir.
Durumu yukarıda çıkarma
Prop delme
Eğer verileri ağaçtaki bileşenlere "teleport" etmenin bir yolu olsa ve bunu props geçişi olmadan yapsanız harika olmaz mıydı? React'in bağlam özelliği ile bunu gerçekleştirebilirsiniz!
Bağlam: Prop Geçmenin Bir Alternatifi {/context-an-alternative-to-passing-props/}
Bağlam, bir üst bileşenin altındaki tüm ağaç için veri sağlamasına olanak tanır. Bağlam için birçok kullanım vardır. İşte bir örnek. Boyutu için bir level
kabul eden bu Heading
bileşenini düşünün:
import Heading from './Heading.js';
import Section from './Section.js';
export default function Page() {
return (
<Section>
<Heading level={1}>Başlık</Heading>
<Heading level={2}>Başlık</Heading>
<Heading level={3}>Alt Başlık</Heading>
<Heading level={4}>Alt Alt Başlık</Heading>
<Heading level={5}>Alt Alt Alt Başlık</Heading>
<Heading level={6}>Alt Alt Alt Alt Başlık</Heading>
</Section>
);
}
export default function Section({ children }) {
return (
<section className="section">
{children}
</section>
);
}
export default function Heading({ level, children }) {
switch (level) {
case 1:
return <h1>{children}</h1>;
case 2:
return <h2>{children}</h2>;
case 3:
return <h3>{children}</h3>;
case 4:
return <h4>{children}</h4>;
case 5:
return <h5>{children}</h5>;
case 6:
return <h6>{children}</h6>;
default:
throw Error('Bilinmeyen seviye: ' + level);
}
}
.section {
padding: 10px;
margin: 5px;
border-radius: 5px;
border: 1px solid #aaa;
}
Diyelim ki aynı Section
içinde birden fazla başlığın aynı boyutta olmasını istiyorsunuz:
import Heading from './Heading.js';
import Section from './Section.js';
export default function Page() {
return (
<Section>
<Heading level={1}>Başlık</Heading>
<Section>
<Heading level={2}>Başlık</Heading>
<Heading level={2}>Başlık</Heading>
<Heading level={2}>Başlık</Heading>
<Section>
<Heading level={3}>Alt Başlık</Heading>
<Heading level={3}>Alt Başlık</Heading>
<Heading level={3}>Alt Başlık</Heading>
<Section>
<Heading level={4}>Alt Alt Başlık</Heading>
<Heading level={4}>Alt Alt Başlık</Heading>
<Heading level={4}>Alt Alt Başlık</Heading>
</Section>
</Section>
</Section>
</Section>
);
}
export default function Section({ children }) {
return (
<section className="section">
{children}
</section>
);
}
export default function Heading({ level, children }) {
switch (level) {
case 1:
return <h1>{children}</h1>;
case 2:
return <h2>{children}</h2>;
case 3:
return <h3>{children}</h3>;
case 4:
return <h4>{children}</h4>;
case 5:
return <h5>{children}</h5>;
case 6:
return <h6>{children}</h6>;
default:
throw Error('Bilinmeyen seviye: ' + level);
}
}
.section {
padding: 10px;
margin: 5px;
border-radius: 5px;
border: 1px solid #aaa;
}
Şu anda, her `'e
level` prop'unu ayrı ayrı geçiriyorsunuz:
<Section>
<Heading level={3}>Hakkında</Heading>
<Heading level={3}>Fotoğraflar</Heading>
<Heading level={3}>Videolar</Heading>
</Section>
bileşenine `level` prop'unu verip
'den çıkarmanız güzel olurdu. Bu sayede, aynı bölümdeki tüm başlıkların aynı boyutta olmasını garanti edebilirsiniz:
<Section level={3}>
<Heading>Hakkında</Heading>
<Heading>Fotoğraflar</Heading>
<Heading>Videolar</Heading>
</Section>
Ama bileşeni en yakın
'in seviyesini nasıl bilecek? Bu, çocuk bileşenin ağaçta yukarıda bir yerden veri "istemesi" için bir yol gerektiriyor.
Sadece props ile bunu yapamazsınız. İşte burada bağlam devreye giriyor. Bunu üç adımda gerçekleştireceksiniz:
- Bir bağlam oluşturun. (Buna
LevelContext
diyebilirsiniz, çünkü başlık seviyesidir.) - Veri ihtiyacı olan bileşende bu bağlamı kullanın. (
Heading
,LevelContext
'i kullanacaktır.) - Veriyi belirten bileşenden bu bağlamı sağlayın. (
Section
,LevelContext
'i sağlayacaktır.)
Bağlam, bir üst bileşenin—hatta uzaktaki birinin!—içindeki tüm ağaç için bazı veriler sağlamasına olanak tanır.
Bağlamı yakın çocuklarda kullanma
Bağlamı uzak çocuklarda kullanma
Adım 1: Bağlamı Oluştur {/step-1-create-the-context/}
Öncelikle, bağlamı oluşturmanız gerekiyor. Bileşenlerinizin bunu kullanması için bir dosyadan dışa aktarmanız gerekecek:
import Heading from './Heading.js';
import Section from './Section.js';
export default function Page() {
return (
<Section>
<Heading level={1}>Başlık</Heading>
<Section>
<Heading level={2}>Başlık</Heading>
<Heading level={2}>Başlık</Heading>
<Heading level={2}>Başlık</Heading>
<Section>
<Heading level={3}>Alt Başlık</Heading>
<Heading level={3}>Alt Başlık</Heading>
<Heading level={3}>Alt Başlık</Heading>
<Section>
<Heading level={4}>Alt Alt Başlık</Heading>
<Heading level={4}>Alt Alt Başlık</Heading>
<Heading level={4}>Alt Alt Başlık</Heading>
</Section>
</Section>
</Section>
</Section>
);
}
export default function Section({ children }) {
return (
<section className="section">
{children}
</section>
);
}
export default function Heading({ level, children }) {
switch (level) {
case 1:
return <h1>{children}</h1>;
case 2:
return <h2>{children}</h2>;
case 3:
return <h3>{children}</h3>;
case 4:
return <h4>{children}</h4>;
case 5:
return <h5>{children}</h5>;
case 6:
return <h6>{children}</h6>;
default:
throw Error('Bilinmeyen seviye: ' + level);
}
}
import { createContext } from 'react';
export const LevelContext = createContext(1);
.section {
padding: 10px;
margin: 5px;
border-radius: 5px;
border: 1px solid #aaa;
}
createContext
'in tek argümanı varsayılan değerdir. Burada 1
, en büyük başlık seviyesine işaret eder, ancak herhangi bir tür değer (hatta bir nesne bile) geçebilirsiniz. Önceki adımda varsayılan değerin önemiyle ilgili bilgi edineceksiniz.
Adım 2: Bağlamı Kullanın {/step-2-use-the-context/}
React'ten useContext
Hook'unu ve bağlamınızı içe aktarın:
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';
Şu anda, Heading
bileşeni level
'i props'tan okuyor:
export default function Heading({ level, children }) {
// ...
}
Bunun yerine, level
prop'unu kaldırın ve içe aktardığınız bağlamdan, LevelContext
'ten değeri okuyun:
export default function Heading({ children }) {
const level = useContext(LevelContext);
// ...
}
useContext
bir Hook'tur. useState
ve useReducer
gibi, bir Hook'u yalnızca bir React bileşeninin içinde hemen çağırabilirsiniz (döngüler veya koşullar içinde değil). useContext
, React'e Heading
bileşeninin LevelContext
'i okumak istediğini bildirir.
Artık Heading
bileşeninin bir level
prop'u olmadığından, JSX'inizde yukarıdaki gibi Heading
'e level
prop'unu geçmenize gerek kalmaz:
<Section>
<Heading level={4}>Alt Alt Başlık</Heading>
<Heading level={4}>Alt Alt Başlık</Heading>
<Heading level={4}>Alt Alt Başlık</Heading>
</Section>
JSX'i güncelleyin, böylece bu sefer Section
onu alsın:
<Section level={4}>
<Heading>Alt Alt Başlık</Heading>
<Heading>Alt Alt Başlık</Heading>
<Heading>Alt Alt Başlık</Heading>
</Section>
Hatırlatma olması açısından, çalışmasını istediğiniz markup şöyle:
import Heading from './Heading.js';
import Section from './Section.js';
export default function Page() {
return (
<Section level={1}>
<Heading>Başlık</Heading>
<Section level={2}>
<Heading>Başlık</Heading>
<Heading>Başlık</Heading>
<Heading>Başlık</Heading>
<Section level={3}>
<Heading>Alt Başlık</Heading>
<Heading>Alt Başlık</Heading>
<Heading>Alt Başlık</Heading>
<Section level={4}>
<Heading>Alt Alt Başlık</Heading>
<Heading>Alt Alt Başlık</Heading>
<Heading>Alt Alt Başlık</Heading>
</Section>
</Section>
</Section>
</Section>
);
}
export default function Section({ children }) {
return (
<section className="section">
{children}
</section>
);
}
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';
export default function Heading({ children }) {
const level = useContext(LevelContext);
switch (level) {
case 1:
return <h1>{children}</h1>;
case 2:
return <h2>{children}</h2>;
case 3:
return <h3>{children}</h3>;
case 4:
return <h4>{children}</h4>;
case 5:
return <h5>{children}</h5>;
case 6:
return <h6>{children}</h6>;
default:
throw Error('Bilinmeyen seviye: ' + level);
}
}
import { createContext } from 'react';
export const LevelContext = createContext(1);
.section {
padding: 10px;
margin: 5px;
border-radius: 5px;
border: 1px solid #aaa;
}
Bu örneğin henüz tam olarak çalışmadığını unutmayın! Tüm başlıklar aynı boyutta çünkü bağlamı sağlamadınız. React, nereden alacağını bilmez!
Eğer bağlamı sağlamazsanız, React daha önce belirttiğiniz varsayılan değeri kullanacaktır. Bu örnekte createContext
'e 1
argümanını belirttiğiniz için, useContext(LevelContext)
1
döner ve tüm başlıkları `yapar. Bu durumu çözmek için her
Section`'ın kendi bağlamını sağlamasını sağlayalım.
Adım 3: Bağlamı Sağlayın {/step-3-provide-the-context/}
Section
bileşeni şu anda çocuklarını render ediyor:
export default function Section({ children }) {
return (
<section className="section">
{children}
</section>
);
}
Onları bir bağlam sağlayıcı ile sarmalayın ki LevelContext
'i sağlamış olun:
import { LevelContext } from './LevelContext.js';
export default function Section({ level, children }) {
return (
<section className="section">
<LevelContext.Provider value={level}>
{children}
</LevelContext.Provider>
</section>
);
}
Bu, React'e "bu içinde herhangi bir bileşen `LevelContext` için bir istek yaparsa, onlara bu `level` değerini ver" demektir. Bileşen, en yakın
'ın değerini kullanır.
import Heading from './Heading.js';
import Section from './Section.js';
export default function Page() {
return (
<Section level={1}>
<Heading>Başlık</Heading>
<Section level={2}>
<Heading>Başlık</Heading>
<Heading>Başlık</Heading>
<Heading>Başlık</Heading>
<Section level={3}>
<Heading>Alt Başlık</Heading>
<Heading>Alt Başlık</Heading>
<Heading>Alt Başlık</Heading>
<Section level={4}>
<Heading>Alt Alt Başlık</Heading>
<Heading>Alt Alt Başlık</Heading>
<Heading>Alt Alt Başlık</Heading>
</Section>
</Section>
</Section>
</Section>
);
}
import { LevelContext } from './LevelContext.js';
export default function Section({ level, children }) {
return (
<section className="section">
<LevelContext.Provider value={level}>
{children}
</LevelContext.Provider>
</section>
);
}
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';
export default function Heading({ children }) {
const level = useContext(LevelContext);
switch (level) {
case 1:
return <h1>{children}</h1>;
case 2:
return <h2>{children}</h2>;
case 3:
return <h3>{children}</h3>;
case 4:
return <h4>{children}</h4>;
case 5:
return <h5>{children}</h5>;
case 6:
return <h6>{children}</h6>;
default:
throw Error('Bilinmeyen seviye: ' + level);
}
}
import { createContext } from 'react';
export const LevelContext = createContext(1);
.section {
padding: 10px;
margin: 5px;
border-radius: 5px;
border: 1px solid #aaa;
}
Aynı sonuca varıyorsunuz ama artık her Heading
bileşenine level
prop'unu geçmek zorunda değilsiniz! Bunun yerine, en yakın Section
'dan "eğitim" alarak seviyesini öğreniyor: