useTransition, kullanıcı arayüzü (UI) işlemlerini engellemeden state güncellemelerini gerçekleştirebilmenizi sağlayan bir React Hook’tur.

const [isPending, startTransition] = useTransition()

Başvuru dokümanı

useTransition()

Bazı state güncellemelerini transition (ertelenen güncelleme) olarak işaretlemek için, bileşeninizin en üst seviyesinde useTransition’ı çağırın.

import { useTransition } from 'react';

function TabContainer() {
const [isPending, startTransition] = useTransition();
// ...
}

Daha fazla örnek için aşağıya bakınız.

Parametreler

useTransition parametre almaz.

Dönen değerler

useTransition, tam olarak iki elemanlı dizi döndürür:

  1. Transition işleminin beklenip beklenmediğini söyleyen isPending belirteci.
  2. State güncellemesini transition olarak işaretlemenizi sağlayan startTransition fonksiyonu.

startTransition fonksiyonu

useTransition tarafından döndürülen startTransition fonksiyonu, bir state güncellemesini transition (ertelenen güncelleme) olarak işaretlemenize olanak tanır.

function TabContainer() {
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('about');

function selectTab(nextTab) {
startTransition(() => {
setTab(nextTab);
});
}
// ...
}

Parametreler

  • scope: Bir veya birden fazla set fonksiyonu kullanarak bazı state’leri güncelleyen bir fonksiyondur. React, scope fonksiyon çağrısı sırasında eş zamanlı olarak planlanan tüm state güncellemelerini transition olarak işaretler ve herhangi bir parametre olmaksızın scope‘u hemen çalıştırır. Bu güncellemeler engelleme yapmaz (non-blocking) ve gereksiz yükleme animasyonları göstermez.

Dönen değerler

startTransition herhangi bir şey geri döndürmez.

Uyarılar

  • useTransition bir Hook olduğu için yalnızca bileşenlerin içinde veya özel Hook’ların içinde çağrılabilir. Eğer bir transition işlemini başka bir yerden başlatmanız gerekiyorsa (örneğin, bir veri kütüphanesinden), bunun yerine bağımsız startTransition’ı çağırın.

  • Bir güncellemeyi transition olarak kullanmak için, ilgili state’in set fonksiyonuna erişebilmeniz gerekiyor. Eğer bir prop veya özel bir Hook dönüş değerine yanıt olarak transition başlatmak isterseniz, bunun yerine useDeferredValue özelliğini kullanmayı deneyebilirsiniz.

  • startTransition‘a ilettiğiniz fonksiyon, eşzamanlı olarak çalışabilecek bir fonksiyon olmalıdır. React, bu fonksiyonu hemen çalıştırır ve çalışırken gerçekleşen tüm state güncellemelerini transition olarak işaretler. Sonrasında daha fazla state güncellemesi yapmaya çalışırsanız (örneğin, bir zaman aşımında), bunlar transition olarak işaretlenmezler.

  • Bir state güncelleme işlemi transition olarak işaretlendiğinde, diğer güncelleme işlemleri bu işlemi kesintiye uğratabilir. Örneğin, bir grafik bileşenini güncelleyen transition işlemi sırasında, grafik bileşeni tekrar render işlemi devam ederken bir giriş alanına yazmaya başlarsanız, React, giriş alanındaki güncellemeyi işledikten sonra tekrar render işlemini başlatır.

  • Transition güncellemeleri, metin girişlerini kontrol etmek için kullanılamaz.

  • Eğer birden fazla transition işlemi devam ediyorsa, React şu an için bu güncellemeleri birleştirir. Ancak bu durum, ileride kaldırılması beklenen bir kısıtlamadır.


Kullanım

Bir state güncellemesini, gecikmeye neden olmayan transition olarak işaretlemek.

State güncellemelerini transition olarak işaretlemek için, bileşeninizin en üst seviyesinde useTransition‘ı çağırın.

import { useState, useTransition } from 'react';

function TabContainer() {
const [isPending, startTransition] = useTransition();
// ...
}

useTransition, tam olarak iki elemanlı dizi döndürür:

  1. Transition işleminin beklenip beklenmediğini söyleyen isPending belirteci.
  2. State güncellemesini transition olarak işaretlemenizi sağlayan startTransition fonksiyonu.

Sonra state güncellemesini bu şekilde transition olarak işaretleyebilirsiniz:

function TabContainer() {
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('about');

function selectTab(nextTab) {
startTransition(() => {
setTab(nextTab);
});
}
// ...
}

Transition’lar, kullanıcı arayüzü güncellemelerini yavaş cihazlarda bile hızlı ve duyarlı tutmanıza olanak tanır.

Transition’lar ile, kullanıcı arayüzü yeniden render sırasında bile duyarlı kalır. Örneğin, kullanıcı bir sekmeye tıklar, ancak sonra fikrini değiştirir ve başka bir sekmeye tıklarsa, bunu birinci tekrar render işleminin tamamlanmasını beklemeden yapabilir.

useTransition ve standart state güncellemeleri arasındaki fark

Örnek 1 / 2:
Transition ile aktif sekmeyi güncelleme

Bu örnekte, “Posts” sekmesi bilinçli olarak yavaşlatılmıştır, böylece render işleminin tamamlanması en az bir saniye sürecektir.

”Posts” sekmesine tıkladıktan sonra hemen “Contact” sekmesine tıklarsanız, yavaş olan “Posts” sekmesinin render işleminin durduğunu fark edeceksiniz. “Contact” sekmesi hemen gösterilir. State güncellemesi transition olarak işaretlendiği için, yavaş bir yeniden render işlemi kullanıcı arayüzünü dondurmadı.

import { useState, useTransition } from 'react';
import TabButton from './TabButton.js';
import AboutTab from './AboutTab.js';
import PostsTab from './PostsTab.js';
import ContactTab from './ContactTab.js';

export default function TabContainer() {
  const [isPending, startTransition] = useTransition();
  const [tab, setTab] = useState('about');

  function selectTab(nextTab) {
    startTransition(() => {
      setTab(nextTab);
    });
  }

  return (
    <>
      <TabButton
        isActive={tab === 'about'}
        onClick={() => selectTab('about')}
      >
        About
      </TabButton>
      <TabButton
        isActive={tab === 'posts'}
        onClick={() => selectTab('posts')}
      >
        Posts (slow)
      </TabButton>
      <TabButton
        isActive={tab === 'contact'}
        onClick={() => selectTab('contact')}
      >
        Contact
      </TabButton>
      <hr />
      {tab === 'about' && <AboutTab />}
      {tab === 'posts' && <PostsTab />}
      {tab === 'contact' && <ContactTab />}
    </>
  );
}


Transition kullanarak, üst bileşenin güncellenmesi.

useTransition çağrısı ile birlikte, bir üst bileşenin state’ini de güncelleyebilirsiniz. Örneğin, TabButton bileşeni, onClick işlemini transition içine alır:

export default function TabButton({ children, isActive, onClick }) {
const [isPending, startTransition] = useTransition();
if (isActive) {
return <b>{children}</b>
}
return (
<button onClick={() => {
startTransition(() => {
onClick();
});
}}>
{children}
</button>
);
}

Üst bileşen, onClick olay işleyicisi (event handler) içinde state’i güncellediği için, state güncellemesi transition olarak işaretlenir. Bu sayede, daha önceki örnekte olduğu gibi, “Posts” sekmesine tıklayabilir ve hemen ardından “Contact”a tıklayabilirsiniz. Seçili sekmenin güncellenmesi transition olarak işaretlendiğinden kullanıcı etkileşimleri engellenmez.

import { useTransition } from 'react';

export default function TabButton({ children, isActive, onClick }) {
  const [isPending, startTransition] = useTransition();
  if (isActive) {
    return <b>{children}</b>
  }
  return (
    <button onClick={() => {
      startTransition(() => {
        onClick();
      });
    }}>
      {children}
    </button>
  );
}


Transition sırasında beklenen görsel state’in gösterimi

useTransition tarafından döndürülen isPending boolean değerini kullanarak, bir transition işleminin hala devam ettiğini kullanıcıya gösterebilirsiniz. Örneğin, sekme düğmesi özel bir “pending” (beklemede) görsel state’ine sahip olabilir:

function TabButton({ children, isActive, onClick }) {
const [isPending, startTransition] = useTransition();
// ...
if (isPending) {
return <b className="pending">{children}</b>;
}
// ...

“Posts”a tıkladığınızda, sekme düğmesinin anında güncellenmesi sebebiyle daha hızlı bir yanıt verdiğini göreceksiniz:

import { useTransition } from 'react';

export default function TabButton({ children, isActive, onClick }) {
  const [isPending, startTransition] = useTransition();
  if (isActive) {
    return <b>{children}</b>
  }
  if (isPending) {
    return <b className="pending">{children}</b>;
  }
  return (
    <button onClick={() => {
      startTransition(() => {
        onClick();
      });
    }}>
      {children}
    </button>
  );
}


İstenmeyen yükleme göstergelerinin engellenmesi

Bu örnekte, PostsTab bileşeni, Suspense özelliği etkinleştirilmiş bir veri kaynağını kullanarak bazı verileri getirir. “Posts” sekmesine tıkladığınızda, PostsTab bileşeni askıya alınır (suspends) ve en yakın yükleme (loading) yedeklemesinin görünmesine neden olur.

import { Suspense, useState } from 'react';
import TabButton from './TabButton.js';
import AboutTab from './AboutTab.js';
import PostsTab from './PostsTab.js';
import ContactTab from './ContactTab.js';

export default function TabContainer() {
  const [tab, setTab] = useState('about');
  return (
    <Suspense fallback={<h1>🌀 Loading...</h1>}>
      <TabButton
        isActive={tab === 'about'}
        onClick={() => setTab('about')}
      >
        About
      </TabButton>
      <TabButton
        isActive={tab === 'posts'}
        onClick={() => setTab('posts')}
      >
        Posts
      </TabButton>
      <TabButton
        isActive={tab === 'contact'}
        onClick={() => setTab('contact')}
      >
        Contact
      </TabButton>
      <hr />
      {tab === 'about' && <AboutTab />}
      {tab === 'posts' && <PostsTab />}
      {tab === 'contact' && <ContactTab />}
    </Suspense>
  );
}

Tüm sekme içeriğini gizlemek ve bir yüklenme göstergesi göstermek, kullanıcı deneyiminde rahatsız edici bir etkiye neden olabilir. TabButton’a useTransition eklerseniz, bunun yerine bekleyen state’i sekme düğmesinde gösterebilirsiniz.

Artık “Posts”a tıklamanın tüm sekme konteynırını bir döndürücüyle (spinner) değiştirmediğini fark edeceksiniz:

import { useTransition } from 'react';

export default function TabButton({ children, isActive, onClick }) {
  const [isPending, startTransition] = useTransition();
  if (isActive) {
    return <b>{children}</b>
  }
  if (isPending) {
    return <b className="pending">{children}</b>;
  }
  return (
    <button onClick={() => {
      startTransition(() => {
        onClick();
      });
    }}>
      {children}
    </button>
  );
}

Suspense ile transition kullanımı hakkında daha fazla bilgi edinin.

Not

Transition’lar, zaten görünen içeriği (örneğin sekme kutusu gibi) gizlememek için yeterli süre boyunca “bekler”. Eğer “Posts” sekmesinde iç içe geçmiş <Suspense> sınırlaması bulunuyorsa, transition onun için “bekleme” yapmaz.


Suspense özelliği etkinleştirilmiş yönlendirici oluşturma

Eğer bir React çatısı (framework) veya yönlendirici oluşturuyorsanız, sayfa gezinmelerini transition’lar olarak işaretlemenizi öneririz.

function Router() {
const [page, setPage] = useState('/');
const [isPending, startTransition] = useTransition();

function navigate(url) {
startTransition(() => {
setPage(url);
});
}
// ...

Bu, iki nedenden dolayı önerilir:

İşte, gezinmeler için transitionlar kullanarak yapılmış küçük bir basitleştirilmiş yönlendirici örneği.

import { Suspense, useState, useTransition } from 'react';
import IndexPage from './IndexPage.js';
import ArtistPage from './ArtistPage.js';
import Layout from './Layout.js';

export default function App() {
  return (
    <Suspense fallback={<BigSpinner />}>
      <Router />
    </Suspense>
  );
}

function Router() {
  const [page, setPage] = useState('/');
  const [isPending, startTransition] = useTransition();

  function navigate(url) {
    startTransition(() => {
      setPage(url);
    });
  }

  let content;
  if (page === '/') {
    content = (
      <IndexPage navigate={navigate} />
    );
  } else if (page === '/the-beatles') {
    content = (
      <ArtistPage
        artist={{
          id: 'the-beatles',
          name: 'The Beatles',
        }}
      />
    );
  }
  return (
    <Layout isPending={isPending}>
      {content}
    </Layout>
  );
}

function BigSpinner() {
  return <h2>🌀 Loading...</h2>;
}

Not

Suspense özelliği etkinleştirilmiş yönlendiricilerin, varsayılan olarak gezinme güncellemelerini transitionlara dahil etmeleri beklenir.


Bir hata sınırı ile kullanıcılara bir hatayı gösterme

Canary

Error Boundary for useTransition is currently only available in React’s canary and experimental channels. Learn more about React’s release channels here.

If a function passed to startTransition throws an error, you can display an error to your user with an error boundary. To use an error boundary, wrap the component where you are calling the useTransition in an error boundary. Once the function passed to startTransition errors, the fallback for the error boundary will be displayed.

import { useTransition } from "react";
import { ErrorBoundary } from "react-error-boundary";

export function AddCommentContainer() {
  return (
    <ErrorBoundary fallback={<p>⚠️Something went wrong</p>}>
      <AddCommentButton />
    </ErrorBoundary>
  );
}

function addComment(comment) {
  // For demonstration purposes to show Error Boundary
  if (comment == null) {
    throw new Error("Example Error: An error thrown to trigger error boundary");
  }
}

function AddCommentButton() {
  const [pending, startTransition] = useTransition();

  return (
    <button
      disabled={pending}
      onClick={() => {
        startTransition(() => {
          // Intentionally not passing a comment
          // so error gets thrown
          addComment();
        });
      }}
    >
      Add comment
    </button>
  );
}


Sorun Giderme

Transition içinde bir input (giriş) alanını güncelleme işlemi çalışmaz

Bir input alanını kontrol eden state değişkeni için transition kullanamazsınız:

const [text, setText] = useState('');
// ...
function handleChange(e) {
// ❌ Kontrollü input state'i için transitionlar kullanılamaz
startTransition(() => {
setText(e.target.value);
});
}
// ...
return <input value={text} onChange={handleChange} />;

Bunun nedeni, transition işlemlerinin bloklamayan bir yapıda olmalarıdır, ancak bir değişiklik olayına yanıt olarak input alanını güncellemek eşzamanlı olarak gerçekleşmelidir. Yazma işlemine yanıt olarak transition çalıştırmak isterseniz, iki seçeneğiniz vardır:

  1. İki ayrı state değişkeni tanımlayabilirsiniz: biri input state’i için (her zaman eşzamanlı olarak güncellenir), diğeri de bir transition güncelleyeceğiniz değişken. Bu şekilde, girişi eşzamanlı state kullanarak kontrol etmenizi ve transition state değişkenini (girişin “gerisinde kalacak” olan) render işleminize aktarmanızı sağlar.

  2. Alternatif olarak, bir state değişkeniniz olabilir ve gerçek değerin “gerisinde kalacak” olan useDeferredValue ekleyebilirsiniz. Bu, yeni değeri otomatik olarak “yakalamak” için bloklamayan yeniden render işlemini tetikler.


React, state güncellememi bir transition olarak işlemiyor

State güncellemesini bir transition içine aldığınızda, bunun startTransition çağrısı esnasında gerçekleştiğinden emin olun:

startTransition(() => {
// ✅ State'in startTransition çağrısı *esnasında* ayarlanması
setPage('/about');
});

startTransition’a ilettiğiniz fonksiyon senkron olmalıdır.

Bir güncellemeyi bu şekilde transition olarak işaretleyemezsiniz:

startTransition(() => {
// ❌ startTransition çağrısından *sonra* state'in ayarlanması
setTimeout(() => {
setPage('/about');
}, 1000);
});

Onun yerine, bunu yapabilirsiniz:

setTimeout(() => {
startTransition(() => {
// ✅ startTransition çağrısı *esnasında* state'in ayarlanması
setPage('/about');
});
}, 1000);

Benzer şekilde, bu şekilde bir güncellemeyi transition olarak işaretleyemezsiniz:

startTransition(async () => {
await someAsyncFunction();
// ❌ Setting state *after* startTransition call
setPage('/about');
});

Ancak, aşağıdaki şekilde işe yarar:

await someAsyncFunction();
startTransition(() => {
// ✅ startTransition çağrısı *esnasında* state'in ayarlanması
setPage('/about');
});

Bileşenin dışından useTransition’u çağırmak istiyorum

useTransition, bir Hook olduğu için bileşenin dışından çağrılamaz. Bu durumlarda, startTransition adlı bağımsız bir metod kullanabilirsiniz. Bu yöntem aynı şekilde çalışır, ancak isPending belirteçini sağlamaz.


startTransition’a ilettiğim fonksiyon hemen çalışıyor

Bu kodu çalıştırırsanız, 1, 2, 3 yazdırır:

console.log(1);
startTransition(() => {
console.log(2);
setPage('/about');
});
console.log(3);

1, 2, 3 yazdırması beklenir. startTransition’a ilettiğiniz fonksiyon gecikmez. Tarayıcının setTimeout metodu aksine, callback’i daha sonra çalıştırmaz. React, fonksiyonunuzu hemen çalıştırır, ancak çalışırken planlanan herhangi bir state güncellemesi transition olarak işaretlenir. Bunu nasıl çalıştığını aşağıdaki gibi düşünebilirsiniz:

// React'in nasıl çalıştığına dair basitleştirilmiş bir versiyon

let isInsideTransition = false;

function startTransition(scope) {
isInsideTransition = true;
scope();
isInsideTransition = false;
}

function setState() {
if (isInsideTransition) {
// ... bir transition state güncellemesi planla ...
} else {
// ... acil bir state güncellemesi planla ...
}
}