همانطور که در پست قبلی صحبت کردیم در React 18 ویژگیهایی معرفی شده است که توسط رندر شدن concurrent جدید، با یک استراتژی پذیرش تدریجی و آهسته برای برنامههای موجود ارائه میشود. در این پست قصد داریم تا درمورد مراحل و نحوه آپدیت به نسخه React 18 صحبت کنیم.
نکته برای کاربران React Native: ریاکت ۱۸ در نسخه بعدی React Native عرضه خواهد شد. این موضوع به این دلیل است که React 18 برای بهرهمندی از قابلیتهای جدید ارائه شده در این پست به معماری جدید React Native وابسته است. برای اطلاعات بیشتر، سخنرانی اصلی React Conf را ببینید.
برای نصب آخرین آپدیت، یعنی نسخه ۱۸ React از کد زیر استفاده میکنیم:
npm install react react-dom
اما اگر از yarn استفاده میکنیم به این شکل خواهد بود:
yarn add react react-dom
هنگامی که برای اولین بار React 18 را نصب میکنیم، یک اخطار در کنسول خواهیم دید:
ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it’s running React 17. Learn more: https://reactjs.org/link/switch-to-createroot
در این آپدیت، React 18 یک root API جدید معرفی میکند که ارگونومی بهتری برای مدیریت rootها ارائه میدهد. root API جدید همچنین رندر concurrent را فعال میکند که این امکان را به ما میدهد تا ویژگیهای concurrent را انتخاب کنیم.
// Before import { render } from 'react-dom'; const container = document.getElementById('app'); render(<App tab="home" />, container); // After import { createRoot } from 'react-dom/client'; const container = document.getElementById('app'); const root = createRoot(container); root.render(<App tab="home" />);
همچنین unmountComponentAtNode به root.unmount تغییر یافته است:
// Before unmountComponentAtNode(container); // After root.unmount();
همینطور callback از رندر حذف شده است، زیرا معمولاً هنگام استفاده از Suspense نتیجه مورد انتظار را ندارد:
// Before const container = document.getElementById('app'); ReactDOM.render(<App tab="home" />, container, () => { console.log('rendered'); }); // After function AppWithCallbackAfterRender() { useEffect(() => { console.log('rendered'); }); return <App tab="home" /> } const container = document.getElementById('app'); const root = ReactDOM.createRoot(container); root.render(<AppWithCallbackAfterRender />);
درنهایت، اگر برنامه ما از رندر شدن سمت سرور با هیدراتاسیون استفاده میکند، باید hydrate را به hydrateRoot ارتقا دهیم:
// Before import { hydrate } from 'react-dom'; const container = document.getElementById('app'); hydrate(<App tab="home" />, container); // After import { hydrateRoot } from 'react-dom/client'; const container = document.getElementById('app'); const root = hydrateRoot(container, <App tab="home" />); // Unlike with createRoot, you don't need a separate root.render() call here.
برای اینکه اطلاعات بیشتری در این زمینه بدست بیاورید، میتوانید صحبتهای کارگروه را اینجا ببینید.
در نسخه React 18، ما APIهای react-dom/server خود را برای پشتیبانی کامل از Suspense در سرور و استریم SSR اصلاح میکنیم. به عنوان بخشی از این تغییرات، API استریم Node قدیمی را که از پخش استریم Suspense افزایشی در سرور پشتیبانی نمیکند، منسوخ میکنیم.
هنگام استفاده از این API اخطاری خواهیم داشت:
در عوض، برای پخش استریم در محیطهای Node، از API زیر استفاده میکنیم:
همچنین در حال معرفی یک API جدید برای پشتیبانی از جریان SSR با Suspense برای محیطهای توسعه مدرن مانند Deno و Cloudflare هستیم:
در نهایت، این API برای ارائه ایمیلها به کار خود ادامه میدهد:
برای اینکه بیشتر در مورد تغییرات موجود در APIهای رندر سرور مطالعه کنید، پست کارگروه در مورد آپدیت به نسخه React 18 در سرور، بررسی عمیق معماری Suspense SSR جدید، و صحبت Shaundai Person در مورد پخش استریم سرور با Suspense در React Conf 2021 را ببینید.
اگر در پروژه خود از TypeScript استفاده میکنیم، باید dependencyهای @types/react و @types/react-dom خود را به آخرین نسخهها بهروزرسانی کنیم. نوع(type)های جدید ایمنتر هستند و مشکلاتی را که قبلاً توسط type checker نادیده گرفته میشد، برطرف میکنند. مهمترین تغییر این است که اکنون باید هنگام تعریف propها، پراپ children بطور صریح فهرست شود، به عنوان مثال:
interface MyButtonProps { color: string; children?: React.ReactNode; }
میتوانید برای اطلاع از لیست کامل تغییرات نوع(type)ها، React 18 typings pull request را ببینید.
همچنین برای اینکه کدهای برنامه خود را سریعتر به تایپهای جدید و ایمنتر انتقال دهیم، میتوانیم از اسکریپت انتقال خودکار برای این کار استفاده کنیم.
در این آپدیت، React 18 بهطور پیشفرض با انجام batching بیشتر، بهبودهای عملکردی خارج از برنامه را اضافه میکند. batching زمانی اتفاق میافتد که React بهروزرسانیهای چند state را در یک رندر مجدد برای عملکرد بهتر گروهبندی میکند. قبل از React 18، ما فقط بهروزرسانیها را در کنترلکنندههای ایونت React دستهبندی میکردیم. بهروزرسانیهای داخل promiseها، setTimeout، کنترلکنندههای ایونت native یا هر ایونت دیگری بهطور پیشفرض در React دستهبندی نمیشدند:
// Before React 18 only React events were batched function handleClick() { setCount(c => c + 1); setFlag(f => !f); // React will only re-render once at the end (that's batching!) } setTimeout(() => { setCount(c => c + 1); setFlag(f => !f); // React will render twice, once for each state update (no batching) }, ۱۰۰۰);
برای شروع آپدیت React 18 با createRoot، همه بهروزرسانیها بدون توجه به منشا آنها، بهطور خودکار دستهبندی میشوند. این به این معنی است که بهروزرسانیهای داخل timeoutها، promiseها، کنترلکنندههای ایونت native یا هر ایونت دیگری مانند بهروزرسانیهای داخل ایونتهای React دستهبندی خواهند شد:
// After React 18 updates inside of timeouts, promises, // native event handlers or any other event are batched. function handleClick() { setCount(c => c + 1); setFlag(f => !f); // React will only re-render once at the end (that's batching!) } setTimeout(() => { setCount(c => c + 1); setFlag(f => !f); // React will only re-render once at the end (that's batching!) }, ۱۰۰۰);
این یک تغییر اساسی است و انتظار داریم تا منجر به رندر شدن کار کمتر و در نتیجه عملکرد بهتر در برنامههای ما شود. برای جلوگیری از batching اتوماتیک میتوانیم از flushSync استفاده کنیم:
import { flushSync } from 'react-dom'; function handleClick() { flushSync(() => { setCounter(c => c + 1); }); // React has updated the DOM by now flushSync(() => { setFlag(f => !f); }); // React has updated the DOM by now }
برای کسب اطلاعات بیشتر در این زمینه، Automatic batching deep dive را ببینید.
در کارگروه آپدیت React 18، ما با نگهدارنده کتابخانهها کار کردیم تا APIهای جدیدی را ایجاد کنیم که برای پشتیبانی از رندر concurrent برای موارد خاص در زمینههایی مانند styleها و حافظههای خارجی مورد نیاز است. برای پشتیبانی از React 18، برخی از کتابخانهها ممکن است نیاز داشته باشند تا به یکی از APIهای زیر سوئیچ کنند:
در آینده، میخواهیم ویژگی جدیدی اضافه کنیم که به React اجازه میدهد تا با حفظ state، بخشهایی از UI را اضافه و حذف کند. به عنوان مثال، زمانی که کاربر برای مدتی از صفحه نمایش دور میشود و به سپس برمیگردد، React باید بتواند بلافاصله همان صفحه قبلی موجود را نشان دهد. برای انجام این کار، React درختان را با استفاده از همان state کامپوننت قبلی جدا کرده و مجددا نصب کند.
این ویژگی عملکرد بهتری را در حالت خارج از برنامه به React میدهد، اما مستلزم این است که کامپوننتها در برابر اثراتی که چندین بار نصب شدن و سپس جدا شدن دربر خواهد داشت، انعطافپذیر باشند. اکثر افکتها بدون هیچ تغییری کار میکنند، اما برخی از آنها فرض میکنند که فقط یک بار نصب یا نابود میشوند.
برای کمک به آشکار شدن این مشکلات،React 18 یک بررسی جدید را که فقط برای توسعه Strict Mode است، معرفی میکند. این بررسی بهطور خودکار هر کامپوننتی را جدا میکند و مجدداً نصب میکند، و هر زمانی که کامپوننتی برای اولین بار نصب شود، state قبلی را هنگامی که برای بار دوم نصب میشود، بازیابی میکند.
قبل از این تغییر، React کامپوننت را نصب و افکتها را ایجاد میکرد:
* React mounts the component. * Layout effects are created. * Effect effects are created.
با Strict Mode در React 18، ریاکت جدا کردن و نصب مجدد کامپوننت را در حالت توسعه شبیهسازی میکند:
* React mounts the component. * Layout effects are created. * Effect effects are created. * React simulates unmounting the component. * Layout effects are destroyed. * Effects are destroyed. * React simulates mounting the component with the previous state. * Layout effect setup code runs * Effect setup code runs
برای اطلاعات بیشتر، به پستهای کارگروه درمورد افزودن state با قابلیت استفاده مجدد به StrictMode و نحوه پشتیبانی از state با قابلیت استفاده مجدد در افکتها مراجعه کنید.
هنگامی که برای اولین بار تستهای خود را برای استفاده از createRoot بهروزرسانی میکنیم، ممکن است این اخطار را در کنسول آزمایشی خود مشاهده کنیم:
The current testing environment is not configured to support act(…)
برای رفع این مشکل قبل از اجرای تست، globalThis.IS_REACT_ACT_ENVIRONMENT را روی true تنظیم میکنیم:
// In your test setup file globalThis.IS_REACT_ACT_ENVIRONMENT = true;
هدف از flag این است تا به React بگوییم که در یک محیط شبیه به تست در حال اجرا است. حتی اگر فراموش کنیم بهروزرسانی را با استفاده از act انجام دهیم، React اخطارهای مفیدی را ثبت خواهد کرد.
همچنین میتوانیم مقدار flag را روی false تنظیم کنیم تا به React بگوییم که act لازم نیست. این موضوع میتواند برای تستهای سرتاسری که یک محیط کامل مرورگر را شبیهسازی میکنند، مفید باشد.
در نهایت، انتظار داریم تا کتابخانههای تست این موضوع را بهطور خودکار برای ما پیکربندی کنند. به عنوان مثال، نسخه بعدی React Testing Library دارای پشتیبانی داخلی از React 18 و بدون هیچگونه پیکربندی اضافی است.
همینطور توضیحات بیشتر در مورد API تست act و تغییرات مرتبط در کارگروه در این لینک موجود میباشد.
در این نسخه، React پشتیبانی از اینترنت اکسپلورر را که در ۱۵ ژوئن ۲۰۲۲ از حالت پشتیبانی خارج میشود، کنار میگذارد. ما اکنون این تغییر را اعمال میکنیم زیرا ویژگیهای جدیدی که در React 18 معرفی شدهاند با استفاده از ویژگیهای مرورگرهای مدرن مانند ریزتسکها ساخته شدهاند که نمیتوانند به اندازه کافی در اینترنت اکسپلورر polyfill شوند.
اگر نیاز به پشتیبانی از اینترنت اکسپلورر داریم، توصیه میشود تا از React 17 استفاده کنیم.
دیدگاهها: