ریاکت ۱۹.۳ منتشر شد؛ از انیمیشنهای یکپارچه تا تحول در رندر سمت سرور
نسخه ۱۹.۳ ریاکت بالاخره به صورت رسمی روی npm در دسترس قرار گرفت! تیم توسعهدهنده در این نسخه، دو قابلیت بسیار جذاب یعنی View Transitions و Fragment Refs را که سال گذشته به شکل آزمایشی معرفی شده بودند، کاملاً پایدار (Stable) کرده است. علاوه بر این، ابزارهای جدیدی برای مدیریت بهتر رندر سمت سرور (SSR) و کامپوننتهای سروری اضافه شده که کار توسعهدهندگان را بسیار راحتتر میکند.
در این مقاله قرار است نگاهی دقیق و البته ساده به این قابلیتهای جدید بیندازیم و با چند مثال کدی ببینیم چطور میتوانیم از آنها در پروژههایمان استفاده کنیم.
۱. انیمیشنهای بومی و روان با View Transitions
احتمالاً برای شما هم پیش آمده که بخواهید یک المان را هنگام ورود یا خروج از صفحه انیمیشندار کنید و مجبور شدهاید سراغ کتابخانههای جانبی بروید. حالا با پایدار شدن کامپوننت <ViewTransition>، ریاکت از API داخلی مرورگرها استفاده میکند تا این کار را به سادهترین شکل ممکن انجام دهد.
کافی است المان مورد نظرتان را داخل <ViewTransition> قرار دهید. از این به بعد، هر بار که یک تغییر State را با استفاده از Transitionها (مثل startTransition) اعمال کنید، ریاکت به صورت خودکار انیمیشن ورود، خروج یا تغییر استایل را اجرا میکند.
مثال یک انیمیشن ساده:
1import { ViewTransition, useState, startTransition } from 'react'; 2 3export default function App() { 4 const [show, setShow] = useState(false); 5 6 return ( 7 <> 8 <button onClick={() => { 9 // تغییر استیت حتماً باید داخل Transition باشد 10 startTransition(() => setShow(prev => !prev)); 11 }}> 12 تغییر وضعیت 13 </button> 14 15 {show && ( 16 <ViewTransition> 17 <div className="box">سلام دنیا!</div> 18 </ViewTransition> 19 )} 20 </> 21 ); 22}
مدیریت جهت انیمیشن با addTransitionType
گاهی اوقات نیاز داریم انیمیشنها بر اساس رفتار کاربر متفاوت باشند. مثلاً در یک اسلایدر عکس، زدن دکمه «بعدی» یک انیمیشن و زدن دکمه «قبلی» انیمیشن دیگری داشته باشد. متد جدید addTransitionType دقیقاً برای همین کار ساخته شده است:
1import { addTransitionType, startTransition } from 'react'; 2 3function nextSlide() { 4 startTransition(() => { 5 addTransitionType('next'); 6 setCurrentSlide(c => c + 1); 7 }); 8}
ترکیب فوقالعاده با Suspense
قابلیت <ViewTransition> با Suspense هم به خوبی کار میکند. یعنی میتوانید پروسه لود شدن دادهها یا حتی تصاویر و فونتها را مدیریت کنید و پس از آماده شدن دیتا، یک انیمیشن نرم از حالت Loading (Fallback) به محتوای اصلی داشته باشید.
۲. کنترل DOM بدون تگهای اضافه با Fragment Refs
یکی از درگیریهای همیشگی در ریاکت زمانی است که میخواهیم به گروهی از المانها دسترسی DOM داشته باشیم (مثلاً برای مدیریت فوکوس یا اسکرول) اما نمیخواهیم یک تگ <div> اضافه دور آنها بپیچیم تا ساختار استایلها به هم نریزد.
در ریاکت ۱۹.۳، بالاخره میتوانیم پراپ ref را مستقیماً به <Fragment> پاس بدهیم. این رفرنس به شما یک شیء به نام FragmentInstance میدهد که با آن میتوانید روی تمام فرزندان آن فرگمنت کارهایی مثل focus(), addEventListener یا scrollIntoView را انجام دهید.
مثال استفاده از Fragment Ref:
1import { useRef, useEffect, Fragment } from 'react'; 2 3function ListComponent({ items }) { 4 const fragmentRef = useRef(null); 5 6 useEffect(() => { 7 // اعمال فوکوس روی المانهای داخل فرگمنت 8 fragmentRef.current.focus(); 9 }, []); 10 11 return ( 12 <Fragment ref="{fragmentRef}"> 13 {items.map(item => ( 14 <h2 key={item.id}>{item.title}</h2> 15 ))} 16 </Fragment> 17 ); 18}
۳. کنترل دقیق SSR با تابع جادویی browser()
وقتی با فریمورکهایی مثل Next.js کار میکنیم که رندر سمت سرور (SSR) دارند، گاهی با کامپوننتهایی روبرو میشویم که فقط باید در مرورگر اجرا شوند (مثلاً چون به window، localStorage یا تایمزون کاربر نیاز دارند). تا پیش از این مجبور بودیم با ساخت استیت isMounted و useEffect این مشکل را دور بزنیم تا خطای Hydration نگیریم.
حالا ریاکت یک API رسمی برای این کار معرفی کرده است: use(browser()).
وقتی این خط را داخل کامپوننت مینویسید، ریاکت در سمت سرور اجرای آن را متوقف میکند (Suspend میشود) و Fallback آن نمایش داده میشود، اما به محض رسیدن به کلاینت، کامپوننت به درستی رندر خواهد شد.
مثال استفاده برای نمایش تایمزون کاربر:
1import { Suspense, use } from 'react'; 2import { browser } from 'react-dom'; 3 4function UserTimeZone() { 5 // جلوگیری از اجرای این خط در سرور 6 use(browser()); 7 8 const timeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone; 9 return <p>تایمزون شما: {timeZone}</p>; 10} 11 12export default function App() { 13 return ( 14 <Suspense fallback="{<p">در حال بارگذاری...</p>}> 15 <UserTimeZone/> 16 </Suspense> 17 ); 18}
۴. راحتی بیشتر در Server Component ها
اگر از React Server Components استفاده میکنید، احتمالاً میدانید که تا قبل از این نسخه، برای رندر کردن یک Context مجبور بودیم یک فایل واسط کلاینتی (با 'use client') بسازیم و یک Provider اختصاصی در آن خروجی بگیریم تا بتوانیم از آن در کامپوننتهای سروری استفاده کنیم.
خبر خوب این است که در نسخه ۱۹.۳، سرور کامپوننتها میتوانند Context را مستقیماً از یک ماژول 'use client' ایمپورت کرده و بدون نیاز به هیچ کامپوننت واسطی (Wrapper)، آن را رندر کنند.
کد در ریاکت ۱۹.۳:
1// فایل user-context.js 2'use client'; 3import { createContext } from 'react'; 4export const UserContext = createContext(null); 5 6// فایل server-component.js 7import { UserContext } from './user-context'; 8 9export async function Layout({ children }) { 10 const currentUser = await getCurrentUser(); 11 12 return ( 13 // رندر مستقیم Context بدون ساخت Provider واسط 14 <UserContext value="{currentUser}"> 15 {children} 16 </UserContext> 17 ) 18}
جمعبندی
نسخه ۱۹.۳ ریاکت با پایدار کردن ابزارهایی مثل ویو ترانزیشنها، نشان میدهد که تیم توسعه قصد دارد نیاز پروژهها به کتابخانههای جانبی و کدهای اضافه (Boilerplate) را به حداقل برساند. رفع باگهای پیچیده مربوط به Hydration و سادهتر شدن ساختار سرور کامپوننتها، تجربه توسعه (DX) را بسیار لذتبخشتر از قبل کرده است.
به نظر میرسد زمان آن رسیده که پروژههایمان را بهروز کنیم و از شر ترفندهای قدیمی مثل isMounted یا div های اضافی خلاص شویم!