useTransition
useTransition est un Hook React qui vous permet de mettre à jour l’état sans bloquer l’UI.
const [isPending, startTransition] = useTransition()- Référence
- Utilisation
- Marquer une mise à jour d’état comme étant une Transition non bloquante
- Mettre à jour le composant parent dans une Transition
- Afficher une indication visuelle pendant la Transition
- Empêcher les indicateurs de chargement indésirables
- Construire un routeur compatible Suspense
- Afficher une erreur à l’utilisateur avec un périmètre d’erreur
- Dépannage
Référence
useTransition()
Appelez useTransition au niveau racine de votre composant pour marquer certaines mises à jour d’état comme étant des Transitions.
import { useTransition } from 'react';
function TabContainer() {
const [isPending, startTransition] = useTransition();
// ...
}Voir d’autres exemples ci-dessous.
Paramètres
useTransition ne prend aucun argument.
Valeur renvoyée
useTransition renvoie un tableau avec exactement deux éléments :
- Le drapeau
isPendingqui vous indique si la Transition est en cours. - La fonction
startTransitionqui vous permet de marquer une mise à jour d’état comme Transition.
La fonction startTransition
La fonction startTransition renvoyée par useTransition vous permet de marquer une mise à jour d’état comme étant une Transition.
function TabContainer() {
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('about');
function selectTab(nextTab) {
startTransition(() => {
setTab(nextTab);
});
}
// ...
}Paramètres
scope: une fonction qui met à jour l’état en appelant au moins une fonctionset. React appelle immédiatementscopesans argument et marque toutes les mises à jour d’état demandées durant l’exécution synchrone descopecomme des Transitions. Elles seront non bloquantes et n’afficheront pas d’indicateurs de chargement indésirables.
Valeur renvoyée
startTransition ne renvoie rien.
Limitations et points à noter
-
useTransitionest un Hook, il ne peut donc être appelé qu’au sein de composants ou de Hooks personnalisés. Si vous avez besoin de démarrer une Transition à un autre endroit (par exemple, depuis une bibliothèque de gestion de données), utilisez plutôt la fonction autonomestartTransition. -
Vous pouvez enrober une mise à jour dans une Transition uniquement si vous avez accès à la fonction
setde l’état en question. Si vous souhaitez démarrer une Transition en réaction à une prop ou à la valeur renvoyée par un Hook personnalisé, utilisez plutôtuseDeferredValue. -
La fonction que vous passez à
startTransitiondoit être synchrone. React exécute cette fonction immédiatement, et marque toutes les mises à jour demandées lors de son exécution comme des Transitions. Si vous essayez de faire des mises à jour d’état plus tard (par exemple avec un timer), elles ne seront pas marquées comme des Transitions. -
La fonction
startTransitiona une identité stable, elle ne figure donc généralement pas dans les dépendances des Effets, mais l’inclure n’entraînera pas un déclenchement d’Effet superflu. Si le linter vous permet de l’omettre sans erreurs, c’est que cette omission est sans danger. Apprenez-en davantage sur l’allègement des dépendances d’Effets -
Une mise à jour d’état marquée comme une Transition pourra être interrompue par d’autres mises à jour d’état. Par exemple, si vous mettez à jour un composant de graphe au sein d’une Transition, mais commencez alors une saisie dans un champ texte tandis que le graphe est en train de refaire son rendu, React redémarrera le rendu du composant graphe après avoir traité la mise à jour d’état du champ.
-
Les mises à jour en Transition ne peuvent pas être utilisées pour contrôler des champs textuels.
-
Si plusieurs Transitions sont en cours, React les regroupe pour le moment. Cette limitation sera sans doute levée dans une future version.
Utilisation
Marquer une mise à jour d’état comme étant une Transition non bloquante
Appelez useTransition au niveau racine de votre composant pour marquer des mises à jour d’état comme étant des Transitions non bloquantes.
import { useState, useTransition } from 'react';
function TabContainer() {
const [isPending, startTransition] = useTransition();
// ...
}useTransition renvoie un tableau avec exactement deux éléments :
- Le drapeau
isPendingqui vous indique si la Transition est en cours. - La fonction
startTransitionqui vous permet de marquer une mise à jour d’état comme Transition.
Vous pouvez marquer une mise à jour d’état comme étant une Transition de la façon suivante :
function TabContainer() {
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('about');
function selectTab(nextTab) {
startTransition(() => {
setTab(nextTab);
});
}
// ...
}Les Transitions vous permettent de conserver la réactivité des mises à jour d’interface utilisateur, même sur des appareils lents.
Avec une Transition, votre UI reste réactive pendant le rendu. Par exemple, si l’utilisateur clique sur un onglet mais ensuite change d’avis et va sur un autre onglet, il peut le faire sans devoir d’abord attendre que le premier onglet ait fini son rendu.
Exemple 1 sur 2 · Changer l’onglet actif au sein d’une Transition
Dans cet exemple, l’onglet « Articles » est artificiellement ralenti pour que son rendu prenne au moins une seconde.
Cliquez sur « Articles » puis cliquez immédiatement sur « Contact ». Remarquez que ça interrompt le rendu lent d’« Articles ». L’onglet « Contact » est affiché immédiatement. Puisque la mise à jour d’état est marquée comme une Transition, un rendu lent ne gèle pas pour autant l’interface utilisateur.
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')} > À propos </TabButton> <TabButton isActive={tab === 'posts'} onClick={() => selectTab('posts')} > Articles (lent) </TabButton> <TabButton isActive={tab === 'contact'} onClick={() => selectTab('contact')} > Contact </TabButton> <hr /> {tab === 'about' && <AboutTab />} {tab === 'posts' && <PostsTab />} {tab === 'contact' && <ContactTab />} </> ); }
Mettre à jour le composant parent dans une Transition
Vous pouvez tout aussi bien mettre à jour l’état du composant parent depuis un appel à useTransition. Par exemple, le composant TabButton enrobe la logique de son onClick avec une Transition :
export default function TabButton({ children, isActive, onClick }) {
const [isPending, startTransition] = useTransition();
if (isActive) {
return <b>{children}</b>
}
return (
<button onClick={() => {
startTransition(() => {
onClick();
});
}}>
{children}
</button>
);
}Puisque le composant parent met à jour son état au sein du gestionnaire d’événement onClick, cette mise à jour d’état sera marquée comme étant une Transition. C’est pourquoi, comme dans l’exemple précédent, vous pouvez cliquer sur « Articles » puis immédiatement sur « Contact ». Le changement d’onglet est marqué comme étant une Transition : il ne bloque donc pas les interactions utilisateur.
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> ); }
Afficher une indication visuelle pendant la Transition
Vous pouvez utiliser la valeur booléenne isPending renvoyée par useTransition pour indiquer à l’utilisateur qu’une Transition est en cours. Par exemple, le bouton d’onglet peut avoir un état visuel spécial « en cours » :
function TabButton({ children, isActive, onClick }) {
const [isPending, startTransition] = useTransition();
// ...
if (isPending) {
return <b className="pending">{children}</b>;
}
// ...Remarquez que le clic sur « Articles » semble désormais plus réactif parce que le bouton d’onglet lui-même se met à jour immédiatement :
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> ); }
Empêcher les indicateurs de chargement indésirables
Dans cet exemple, le composant PostsTab charge des données en utilisant une source de données compatible Suspense. Lorsque vous cliquez sur l’onglet « Articles », le composant PostsTab suspend, entraînant l’affichage du plus proche contenu de secours :
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>🌀 Chargement...</h1>}> <TabButton isActive={tab === 'about'} onClick={() => setTab('about')} > À propos </TabButton> <TabButton isActive={tab === 'posts'} onClick={() => setTab('posts')} > Articles </TabButton> <TabButton isActive={tab === 'contact'} onClick={() => setTab('contact')} > Contact </TabButton> <hr /> {tab === 'about' && <AboutTab />} {tab === 'posts' && <PostsTab />} {tab === 'contact' && <ContactTab />} </Suspense> ); }
Masquer le conteneur d’onglets dans son intégralité pour afficher un indicateur de chargement entraîne une expérience utilisateur désagréable. Si vous ajoutez useTransition à TabButton, vous pouvez plutôt manifester l’attente en cours dans le bouton d’onglet.
Remarquez que cliquer sur « Articles » ne remplace plus l’ensemble du conteneur d’onglets avec un spinner :
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> ); }
Apprenez-en davantage sur l’utilisation des Transitions avec Suspense.
Construire un routeur compatible Suspense
Si vous construisez un framework React ou un routeur, nous vous recommandons de marquer toutes les navigations de pages comme étant des Transitions.
function Router() {
const [page, setPage] = useState('/');
const [isPending, startTransition] = useTransition();
function navigate(url) {
startTransition(() => {
setPage(url);
});
}
// ...Nous recommandons ça pour deux raisons :
- Les Transitions sont interruptibles, ce qui permet à l’utilisateur de cliquer pour aller ailleurs sans devoir attendre la fin du rendu de son premier choix.
- Les Transitions évitent les indicateurs de chargement indésirables, ce qui vous évite de produire des « clignotements » désagréables lors de la navigation.
Voici un petit exemple de routeur très simplifié utilisant les Transitions pour ses navigations.
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>🌀 Chargement...</h2>; }
Afficher une erreur à l’utilisateur avec un périmètre d’erreur
Si une fonction passée à startTransition lève une erreur, vous pouvez afficher l’erreur à votre utilisateur au moyen d’un périmètre d’erreur. Pour utiliser un périmètre d’erreur, enrobez le composant qui appelle useTransition avec ce périmètre. Lorsque la fonction passée à startTransition lèvera une erreur, le contenu de secours du périmètre d’erreur sera affiché.
import { useTransition } from "react"; import { ErrorBoundary } from "react-error-boundary"; export function AddCommentContainer() { return ( <ErrorBoundary fallback={<p>⚠️ Ça sent le pâté…</p>}> <AddCommentButton /> </ErrorBoundary> ); } function addComment(comment) { // Pour les besoins de la démonstration uniquement if(comment == null) { throw Error('Example Error: An error thrown to trigger error boundary') } } function AddCommentButton() { const [pending, startTransition] = useTransition(); return ( <button disabled={pending} onClick={() => { startTransition(() => { // On ne passe volontairement pas de commentaire // afin d’entraîner une erreur. addComment(); }); }} > Ajouter un commentaire </button> ); }