useTransition
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ı
- Kullanım
- Bir state güncellemesini, gecikmeye neden olmayan transition olarak işaretlemek.
- Transition kullanarak, üst bileşenin güncellenmesi.
- Transition sırasında beklenen görsel state’in gösterimi
- İstenmeyen yükleme göstergelerinin engellenmesi
- Suspense özelliği etkinleştirilmiş yönlendirici oluşturma
- Bir hata sınırı ile kullanıcılara bir hatayı gösterme
- Sorun Giderme
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:
- Transition işleminin beklenip beklenmediğini söyleyen
isPending
belirteci. - 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ızstartTransition
’ı ç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 yerineuseDeferredValue
ö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:
- Transition işleminin beklenip beklenmediğini söyleyen
isPending
belirteci. - 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.
Ö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.
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:
-
Transition’lar kesilebilir olduğundan, kullanıcı yeniden render işleminin tamamlanmasını beklemeden tıklamayı bırakabilir.
-
Transition’lar istenmeyen yükleme göstergelerini engeller, bu da kullanıcının gezinme sırasında rahatsız edici sıçramalardan kaçınmasını sağlar.
İş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>; }
Bir hata sınırı ile kullanıcılara bir hatayı gösterme
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:
-
İ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.
-
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 ...
}
}