İçeriğe geç
Sedat Demir
Geri dön

React 19 use() Hook: Promise ve Context'i Render Sırasında Okuma Rehberi

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


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:

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.


Share this post on:

Sonraki Yazı
React 19 Form Actions ve useActionState: Server-Side Form Yönetiminin Yeni Çağı