ViewTransition: انیمیشن هماهنگ با چرخه رندر React

در فصل بیست‌ودوم دیدیم Transition یعنی یک به‌روزرسانی که لازم نیست فوری و بی‌وقفه کامل شود. ViewTransition یکی از کاربردهای عملی همین مفهوم است: کامپوننتی که به React می‌گوید وقتی بخشی از رابط کاربری عوض می‌شود، این تغییر را به‌جای پرش ناگهانی، با یک انیمیشن نمایش بده.

وضعیت این API

این قابلیت مدت‌ها فقط در کانال‌های Canary و Experimental وجود داشت و در کدهای قدیمی‌تر با نام unstable_ViewTransition دیده می‌شود. از React 19.3 (سپتامبر ۲۰۲۶) در نسخه پایدار (Stable) عرضه شده است. اگر پروژه‌تان روی نسخه‌ای قدیمی‌تر است، این import در آن کار نمی‌کند و باید ابتدا react و react-dom را هم‌نسخه با ۱۹٫۳ یا بالاتر کنید. این قابلیت فقط در DOM کار می‌کند، نه در React Native.

مشکل: تغییر ناگهانی رابط کاربری

وقتی با Conditional Rendering (فصل اول) یک بخش ظاهر یا ناپدید می‌شود، یا یک لیست مرتب می‌شود، تغییر در یک فریم رخ می‌دهد. چشم کاربر باید خودش حدس بزند چه چیزی کجا رفت. انیمیشن این ارتباط را روشن می‌کند. نوشتن دستی آن اما سخت است: باید موقعیت عناصر پیش و پس از تغییر اندازه‌گیری شود و با رندر React هماهنگ بماند.

راه‌حل: پیچیدن بخش موردنظر در ViewTransition

jsx
import { ViewTransition, useState, startTransition } from "react";

function Page() {
  const [show, setShow] = useState(false);

  function toggle() {
    startTransition(() => {
      setShow((current) => !current);
    });
  }

  return (
    <>
      <button onClick={toggle}>نمایش یا پنهان‌سازی</button>
      {show && (
        <ViewTransition>
          <Details />
        </ViewTransition>
      )}
    </>
  );
}

به‌طور پیش‌فرض، ViewTransition یک محو شدن ملایم (Cross-fade) اجرا می‌کند. پشت صحنه از API خود مرورگر به نام View Transition API استفاده می‌شود؛ React فقط تعیین می‌کند کدام عنصر و در چه لحظه‌ای در انیمیشن شرکت کند.

چهار نوع فعال‌سازی

  • enter: یک ViewTransition به درخت اضافه شود.
  • exit: یک ViewTransition از درخت حذف شود.
  • update: محتوا یا استایل فرزندان آن عوض شود.
  • share: دو ViewTransition با نام یکسان داشته باشیم، یکی حذف و دیگری هم‌زمان اضافه شود؛ React آن را مثل یک عنصر مشترک که حرکت کرده نشان می‌دهد.

شرط مهم: فقط در یک Transition فعال می‌شود

این مهم‌ترین نکته درس است. یک setShow معمولی، که طبق فصل بیست‌ودوم یک به‌روزرسانی فوری (Urgent) است، انیمیشنی اجرا نمی‌کند. انیمیشن فقط وقتی فعال می‌شود که تغییر از طریق یکی از این سه راه رخ بدهد:

  • داخل startTransition (یا startTransition برگشتی از useTransition)
  • از طریق useDeferredValue
  • هنگام ظاهر شدن محتوای یک Suspense (فصل بیست‌ویکم)

دلیل این طراحی منطقی است: انیمیشن باید با اولویت و زمان‌بندی Transition هماهنگ باشد و نباید جلوی واکنش فوری به تعامل کاربر را بگیرد. بنابراین آنچه در فصل بیست‌ودوم یاد گرفتیم، پیش‌نیاز مستقیم این درس است.

محل قرارگیری در JSX

برای اینکه انیمیشن‌های enter و exit فعال شوند، ViewTransition باید پیش از اولین گره DOM قرار بگیرد، یعنی عنصر div یا هر تگ دیگر نباید بالاتر از آن و بیرون آن باشد. ساده‌ترین قاعده: ViewTransition را دور خود بخش بگذارید، نه داخل یک div که بخش دیگری را هم پوشانده است.

سفارشی‌سازی با کلاس

می‌توان برای هر نوع فعال‌سازی یک نام کلاس تعیین کرد و ظاهر انیمیشن را با CSS نوشت. مقدار none هم انیمیشن آن نوع را خاموش می‌کند:

jsx
<ViewTransition enter="slide-in" exit="slide-out" default="none">
  <Details />
</ViewTransition>
css
::view-transition-new(.slide-in) {
  animation: slide-from-right 300ms ease-out;
}

::view-transition-old(.slide-out) {
  animation: slide-to-right 300ms ease-in;
}

در اینجا default="none" یعنی هر نوع فعال‌سازی که صریحاً کلاسی برایش تعیین نکرده‌ایم، بدون انیمیشن بماند.

انواع Transition با addTransitionType

گاهی یک کامپوننت باید بداند چرا Transition رخ داده است (مثلاً «به صفحه بعد رفتیم» یا «برگشتیم») تا جهت انیمیشن را درست انتخاب کند. تابع addTransitionType، که آن هم در نسخه ۱۹٫۳ پایدار شده، اجازه می‌دهد داخل startTransition یک برچسب متنی به Transition بچسبانیم، و ViewTransition بر اساس آن برچسب، کلاس متفاوتی به کار ببرد.

دسترس‌پذیری و مرورگرهای ناسازگار

دو نکته که از فصل هجدهم مستقیم می‌آیند. اول، بعضی کاربران در تنظیمات سیستم‌شان حرکت و انیمیشن را کم می‌کنند. باید این ترجیح را رعایت کرد:

css
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

دوم، در مرورگری که View Transition API را پشتیبانی نمی‌کند، به‌روزرسانی همچنان اعمال می‌شود و فقط انیمیشن اجرا نمی‌شود. یعنی انیمیشن یک بهبود تدریجی است، نه شرط درست کار کردن برنامه. به همین دلیل، منطق مهمی نباید به انجام شدن انیمیشن وابسته باشد.

چرا این درس مثال قابل اجرا ندارد

اجرای صحیح این مثال به React 19.3 یا بالاتر و مرورگری با پشتیبانی از View Transition API نیاز دارد، و اثر آن فقط در حرکت دیده می‌شود، نه در متن خروجی. چون نمی‌توان این دو شرط را برای هر محیط تضمین کرد، کدهای این درس را در یک پروژه واقعی خودتان با نسخه مناسب اجرا کنید تا انیمیشن را ببینید.

جمع‌بندی

ViewTransition از React 19.3 پایدار است و تغییرهای رابط کاربری را با کمک View Transition API مرورگر به‌صورت انیمیشن نشان می‌دهد؛ برای enter، exit، update و share. انیمیشن فقط برای تغییرهایی فعال می‌شود که داخل startTransition، useDeferredValue یا ظاهر شدن Suspense رخ بدهند، نه برای یک setState فوری. باید ViewTransition پیش از اولین گره DOM قرار بگیرد، prefers-reduced-motion رعایت شود و منطق برنامه به اجرای انیمیشن وابسته نباشد. در درس بعدی، به Fragment Refs، دومین قابلیتی که در نسخه ۱۹٫۳ پایدار شد، می‌پردازیم.