React 19 use() Hook: Promise ve Context'i Render Sırasında Okuma Rehberi
React 19, bileşen mimarisinde köklü değişiklikler getiren önemli bir sürüm. Bu sürümle birlikte tanıtılan use() hook'u, React'in mevcut hook kurallarını kıran ve asenkron veri okumayı render döngüsünün içine taşıyan benzersiz bir API. Eğer daha önce useEffect içinde veri çekip, useState ile state'e atayıp, loading durumunu manuel yönetmekten yorulduysanız, use() hook'u tam size göre.
Bu yazıda use() hook'unun ne olduğunu, nasıl çalıştığını, Promise ve Context ile kullanımını, mevcut hook'lardan farklarını ve gerçek dünya senaryolarındaki uygulamalarını detaylı şekilde ele alacağız.
use() Hook Nedir?
use(), React 19 ile birlikte gelen ve render sırasında bir kaynağı (resource) okumanızı sağlayan yeni bir hook'tur. Bu kaynak bir Promise veya bir Context olabilir.
import { use } from 'react';
function MyComponent({ dataPromise }) {
const data = use(dataPromise);
return <div>{data.title}</div>;
}İlk bakışta basit görünüyor, ancak bu basitliğin arkasında React'in Suspense mekanizmasıyla derin bir entegrasyon yatıyor. use() bir Promise aldığında, o Promise resolve olana kadar bileşenin render'ını askıya alır (suspend). Bu, React'in Suspense boundary'sine bir fallback göstermesini tetikler.
use() Hook'unun Temel Özellikleri
- Koşullu olarak çağrılabilir: Diğer hook'ların aksine
if,for,try/catchblokları içinde kullanılabilir. - Promise desteği: Asenkron verileri render sırasında senkron gibi okuyabilirsiniz.
- Context desteği:
useContext'in daha esnek bir alternatifidir. - Suspense entegrasyonu: Promise resolve olana kadar otomatik olarak Suspense fallback'i tetikler.
Promise ile use() Kullanımı
Geleneksel Yaklaşım vs. use() Yaklaşımı
Önce geleneksel useEffect + useState yaklaşımına bakalım:
// ❌ Geleneksel yaklaşım - verbose ve hata yönetimi karmaşık
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
setLoading(true);
fetchUser(userId)
.then((data) => {
if (!cancelled) {
setUser(data);
setLoading(false);
}
})
.catch((err) => {
if (!cancelled) {
setError(err);
setLoading(false);
}
});
return () => { cancelled = true; };
}, [userId]);
if (loading) return <Spinner />;
if (error) return <ErrorMessage error={error} />;
return <div>{user.name}</div>;
}Şimdi aynı işlemi use() ile yapalım:
// ✅ use() yaklaşımı - temiz ve deklaratif
import { use, Suspense } from 'react';
function UserProfile({ userPromise }) {
const user = use(userPromise);
return <div>{user.name}</div>;
}
// Üst bileşende kullanım
function UserPage({ userId }) {
const userPromise = fetchUser(userId);
return (
<Suspense fallback={<Spinner />}>
<UserProfile userPromise={userPromise} />
</Suspense>
);
}Fark ortada: loading state yönetimi yok, cleanup fonksiyonu yok, üç ayrı state değişkeni yok. use() Promise'i okur, resolve olana kadar Suspense fallback gösterilir, resolve olduktan sonra bileşen render edilir.
Kritik Nokta: Promise'i Nerede Oluşturmalısınız?
use() hook'unun doğru çalışması için Promise'in bileşenin dışında veya üst bileşende oluşturulması gerekir. Render sırasında her seferinde yeni bir Promise oluşturursanız, sonsuz döngüye girersiniz:
// ❌ YANLIŞ - Her render'da yeni Promise oluşur
function UserProfile({ userId }) {
const user = use(fetchUser(userId)); // Sonsuz döngü!
return <div>{user.name}</div>;
}
// ✅ DOĞRU - Promise üst bileşenden prop olarak gelir
function UserProfile({ userPromise }) {
const user = use(userPromise);
return <div>{user.name}</div>;
}
// ✅ DOĞRU - Promise modül seviyesinde veya cache ile oluşturulur
const userPromise = fetchUser(1);
function UserProfile() {
const user = use(userPromise);
return <div>{user.name}</div>;
}Hata Yönetimi: Error Boundary ile Entegrasyon
Promise reject olduğunda, use() bu hatayı en yakın Error Boundary'ye fırlatır:
import { use, Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
function UserProfile({ userPromise }) {
const user = use(userPromise);
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
function UserPage({ userId }) {
const userPromise = fetchUser(userId);
return (
<ErrorBoundary fallback={<p>Kullanıcı yüklenirken hata oluştu.</p>}>
<Suspense fallback={<Spinner />}>
<UserProfile userPromise={userPromise} />
</Suspense>
</ErrorBoundary>
);
}Bu yapıda loading ve error durumları tamamen deklaratif olarak yönetilir. Bileşen sadece "başarılı" senaryoya odaklanır.
Context ile use() Kullanımı
use() hook'unun ikinci güçlü kullanım alanı Context okumaktır. useContext'in yaptığı her şeyi yapar, ancak bir kritik farkla: koşullu olarak çağrılabilir.
useContext vs use() ile Context Okuma
import { use, useContext, createContext } from 'react';
const ThemeContext = createContext('light');
// useContext ile - koşullu çağrılamaz
function ThemedButton({ showTheme }) {
// ❌ Bu hook kurallarını ihlal eder
// if (showTheme) {
// const theme = useContext(ThemeContext);
// }
const theme = useContext(ThemeContext); // Her zaman çağrılmalı
return showTheme ? <button className={theme}>Tıkla</button> : null;
}
// use() ile - koşullu çağrılabilir!
function ThemedButton({ showTheme }) {
if (showTheme) {
const theme = use(ThemeContext); // ✅ Koşullu kullanım geçerli
return <button className={theme}>Tıkla</button>;
}
return null;
}Gerçek Dünya Örneği: Koşullu Context Okuma
Koşullu Context okuma, özellikle erken return yapılan bileşenlerde büyük kolaylık sağlar:
import { use, createContext } from 'react';
const AuthContext = createContext(null);
const PermissionsContext = createContext([]);
function AdminPanel({ isAdmin }) {
// Admin değilse context'i okumaya gerek yok
if (!isAdmin) {
return <p>Bu alana erişim yetkiniz yok.</p>;
}
// Sadece admin ise context okunur
const auth = use(AuthContext);
const permissions = use(PermissionsContext);
if (!auth.isAuthenticated) {
return <p>Lütfen giriş yapın.</p>;
}
return (
<div>
<h1>Admin Panel</h1>
<p>Hoş geldin, {auth.user.name}</p>
<ul>
{permissions.map((perm) => (
<li key={perm}>{perm}</li>
))}
</ul>
</div>
);
}use() Hook'unun Diğer Hook'lardan Farkları
Hook Kurallarını Kırması
React'in temel hook kurallarından biri şudur: "Hook'ları döngülerin, koşulların veya iç içe fonksiyonların içinde çağırmayın." use() bu kuralın tek istisnasıdır:
function DataDisplay({ items, shouldFetchDetails }) {
// ✅ use() koşullu blokta çalışır
if (shouldFetchDetails) {
const details = use(fetchDetailsPromise);
return <DetailView data={details} />;
}
// ✅ use() döngülerde bile kullanılabilir (dikkatli olun)
return (
<ul>
{items.map((item) => (
<ItemWithData key={item.id} dataPromise={item.dataPromise} />
))}
</ul>
);
}
function ItemWithData({ dataPromise }) {
const data = use(dataPromise);
return <li>{data.name}</li>;
}use() vs useEffect: Ne Zaman Hangisi?
| Özellik | use() |
useEffect |
|---|---|---|
| Çalışma zamanı | Render sırasında | Render sonrasında |
| Suspense desteği | ✅ Otomatik | ❌ Manuel loading state |
| Koşullu kullanım | ✅ | ❌ |
| Side effect | ❌ | ✅ |
| DOM manipülasyonu | ❌ | ✅ |
| Veri çekme (data fetching) | ✅ (Promise ile) | ✅ (geleneksel yol) |
Temel kural: Veri okuma için use(), yan etkiler (DOM manipülasyonu, event listener, subscription) için useEffect kullanın.
İleri Seviye Kullanım Senaryoları
Birden Fazla Promise'i Paralel Okuma
Birden fazla veri kaynağını paralel olarak okumak için Promise.all ile use() kombinasyonunu kullanabilirsiniz:
import { use, Suspense } from 'react';
function Dashboard({ userPromise, postsPromise, statsPromise }) {
// Her biri bağımsız Suspense boundary'de
return (
<div className="dashboard">
<Suspense fallback={<UserSkeleton />}>
<UserInfo userPromise={userPromise} />
</Suspense>
<Suspense fallback={<PostsSkeleton />}>
<RecentPosts postsPromise={postsPromise} />
</Suspense>
<Suspense fallback={<StatsSkeleton />}>
<StatsPanel statsPromise={statsPromise} />
</Suspense>
</div>
);
}
function UserInfo({ userPromise }) {
const user = use(userPromise);
return <div className="user-card">{user.name}</div>;
}
function RecentPosts({ postsPromise }) {
const posts = use(postsPromise);
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
function StatsPanel({ statsPromise }) {
const stats = use(statsPromise);
return <div>Toplam ziyaret: {stats.visits}</div>;
}Bu yapıda her bileşen bağımsız olarak yüklenir. Biri yavaş olsa bile diğerleri render edilir — bu, kullanıcı deneyimi için mükemmeldir.
Server Components ile Entegrasyon
React Server Components ile use() doğal bir uyum içindedir:
// ServerComponent.jsx (Server Component)
import ClientUserProfile from './ClientUserProfile';
async function UserPage({ userId }) {
const userPromise = fetchUser(userId); // Promise oluştur, await etme!
return (
<Suspense fallback={<Spinner />}>
<ClientUserProfile userPromise={userPromise} />
</Suspense>
);
}
// ClientUserProfile.jsx (Client Component)
'use client';
import { use } from 'react';
export default function ClientUserProfile({ userPromise }) {
const user = use(userPromise);
return (
<div>
<h2>{user.name}</h2>
<p>{user.bio}</p>
</div>
);
}Server Component'te Promise oluşturulur ancak await edilmez. Bu Promise, Client Component'e prop olarak geçirilir ve use() ile render sırasında okunur. React, bu Promise'i sunucudan istemciye serialize ederek aktarabilir.
Dikkat Edilmesi Gereken Noktalar ve Best Practices
1. Promise Stabilizasyonu
Her render'da yeni bir Promise oluşturmaktan kaçının. Framework seviyesinde cache mekanizmaları veya useMemo kullanarak Promise'i stabilize edin:
import { useMemo } from 'react';
function UserPage({ userId }) {
// useMemo ile Promise'i stabilize et
const userPromise = useMemo(() => fetchUser(userId), [userId]);
return (
<Suspense fallback={<Spinner />}>
<UserProfile userPromise={userPromise} />
</Suspense>
);
}2. Suspense Boundary Yerleşimi
Suspense boundary'lerini stratejik olarak yerleştirin. Çok üst seviyeye koymak tüm sayfayı loading state'e sokar, çok alt seviyeye koymak ise layout kaymasına neden olabilir.
3. use() Hook'u Sadece Client Component'lerde
use() bir hook olduğu için sadece Client Component'lerde kullanılabilir. Server Component'lerde doğrudan async/await kullanabilirsiniz:
// Server Component - doğrudan await
async function ServerUserProfile({ userId }) {
const user = await fetchUser(userId); // ✅ Server Component'te await geçerli
return <div>{user.name}</div>;
}
// Client Component - use() kullan
'use client';
function ClientUserProfile({ userPromise }) {
const user = use(userPromise); // ✅ Client Component'te use() kullan
return <div>{user.name}</div>;
}Özet ve Sonuç
React 19'un use() hook'u, React ekosisteminde asenkron veri yönetimini temelden değiştiren bir yeniliktir. İşte öne çıkan noktalar:
- Promise okuma:
use()bir Promise'i render sırasında okur ve Suspense ile otomatik olarak entegre çalışır. Loading ve error state yönetimini deklaratif hale getirir. - Context okuma:
useContext'in aksine koşullu bloklar içinde çağrılabilir, bu da daha esnek bileşen yapıları oluşturmayı mümkün kılar. - Hook kurallarının istisnası:
use(),if,for,try/catchiçinde çağrılabilen tek hook'tur. - Server Components uyumu: Sunucu bileşenlerinde oluşturulan Promise'ler, istemci bileşenlerinde
use()ile okunabilir. - Dikkat noktaları: Promise stabilizasyonu, doğru Suspense boundary yerleşimi ve side effect'ler için hâlâ
useEffectkullanılması gerektiğini unutmayın.
use() hook'u, React'in "UI = f(state)" felsefesini asenkron dünyaya taşıyan güçlü bir araçtır. Eğer henüz React 19'a geçmediyseniz, bu hook tek başına geçiş için güçlü bir motivasyon kaynağı olabilir. Özellikle veri yoğun uygulamalarda, bileşenlerinizin okunabilirliğini ve bakım kolaylığını dramatik şekilde artıracaktır.