نگاهی به نسخه جدید ری‌اکت ۱۶.۶ و نسخه آینده آن

تقریبا یکسال از اولین نوشته من تو ویرگول میگذره، حقیقتا ایده اولیه‌ام برای عضویت تو ویرگول این بود که در مورد خاطرات سربازیم بنویسم که پست اولمم مقدمه همین بود ولی خُب اما از تنبلی حتی همین الان ۳ تا پست تو پیش نویس ها دارم در مورد همین داستان ولی خوب هر کدومشو به دلایلی تکمیل نکردم، تا اینکه امروز تصمیم گرفتم کلا اون داستان رو فراموش کنم چون نوشتن در مورد سربازی شاخ و برگ زیادی داره، و تصمیم گرفتم در مورد چیزای که بلدم هر چند کم بنویسم که اولین پست رو تصمیم گرفتم در مورد React و مخصوصا ویژگی های جدید نسخه ۱۶.۶ اون که چند وقت پیش منتشر شد و نسخه آینده اون ۱۶.۷ که پر از ویژگی های هیجان انگیزه که تو ReactConf2018 معرفی شد بنویسم.

خُب مقدمه چینی بسه بریم سر اصل مطلب ((:

حدود ۹ روز پیش بود که نسخه ۱۶.۶ ری‌اکت منتشر شد که چند تا ویژگی جدید باهاش بود که یجورایی پیش درآمدی برای نسخه بعدی اون یعنی نسخه ۱۶.۷ بود، که Sophie Alpert مدیر تیم ری‌اکتم توییت کرد که لکن دلخوش به این مقدار نباشید ما چیزای دیگه ای هم براتون میسازیم، پس صندلیاتونو صفت بچسبید و منتظر ReactConf2018 باشین.

https://twitter.com/sophiebits/status/1055127361367076864
https://twitter.com/sophiebits/status/1055127361367076864


اول ویژگی های جدید نسخه ۱۶.۶ مثل memo , lazy و قص علی هذا رو در موردش حرف میزنیم بعدش میریم سراغ نسخه ۱۶.۷ .

یِس بالاخره تونستم از قص علی هذا یجا استفاده کنم (((:

ویژگی memo

ری‌اکت تو نسخه ۱۶.۳ بود که ویژگی PureComponent معرفی کرد که کارش این بود که stateها و propsهای جدید رو با قبلیا به صورت shallow مقایسه میکرد و اگر تغییری نکرده بودن اون کامپوننت رو ری‌ رندر نمیکرد در یک کلام متد ‍‍‍‍shouldComponentUpdate به صورت نصفه و نیمه و پیش فرض implement کرده بود و باعث بهتر شدن پرفورمنس اپتون میشد.

نکته : البته اینکه میگم باعث بهتر شدن پرفورمنس اپتون میشه رو خیلی جدی نگیرین چون قرار نیست معجزه کنه براتون (در آینده سعی میکنم در موردش بنویسم)

همه این مقدمه هارو گفتم که به memo برسم، PureComponent خوب یا بد (که قرار نیست در موردش اینجا حرف بزنیم) فقط در دسترس Class Componentها بود و ما نمیتونستیم ازش در Functional Componentها استفاده کنیم، حالا React.memo راهکار ری‌اکت برای استفاده از این ویژگی در Functional Componentها هستش. memo یه HOC هستش که خروجیش یه Memoize Component هست که باعث میشه فقط وقتی کامپوننت ری رندر بشه که props های که داره تغییر بکنه.

https://gist.github.com/theham3d/cf6d3376f11f70803c934b5d71401e45
https://gist.github.com/hesmaili/cf6d3376f11f70803c934b5d71401e45

ویژگی های lazy و Suspence

حتما با Code Splitting آشنا هستین که به ما اجازه میده assetهامون رو به صورت lazy اضافه کنیم، یعنی assetهارو فقط وقتی لود کنیم که قرار باشه ازون ها استفاده کنیم. قبلا ما مجبور بودیم برای اینکار از ابزار های دیگه استفاده کنیم یا اینکه خودمون یه Async Component بنویسیم که هر کدوم دوشواری های خودشو داشت. حالا ری‌اکت HOCای اضافه کرده که اینکار رو خودش به صورت Built-In انجام میده. که اسمش همونجور که از اسم عنوان مشخه lazy هستش.

پس Suspence چیه این وسط، بریم که در مورد این مورد هم یه توضیح بدیم و بعدش یه مثال بزنیم:

وقتی ما assetهامون به صورت dynamic لود میکنیم ممکنه این وسط یخورده وقفه بیفته تا لود بشه یا حتی مشکلی تو لود شدنش بوجود بیاد اینجاست که Suspence بکار میاد و میتونیم با یک loader یا به هر شکل دیگه ای که میخوایم به کاربر اطلاع بدیم داریم اون پشت یه کارای میکنیم و صبر کنه تا نتیجه رو ببینه . خب بریم سراغ مثال :

https://gist.github.com/theham3d/8154c664a18bb79f85cfa958a77f7891
نکته : این ویژکی تو SSR در دسترس نیست و پیشنهاد میشه از react-loadable یا هر چیز دیگه ای که دوست دارین استفاده کنید

متد contextType

تو ری‌اکت ۱۶.۳ بود که Context Api جدید ری‌اکت معرفی شد اگر در موردش آشنا نیستین میتونید اینجا در موردش بخونید. ما برای دسترس به مقدار context باید از روش خاصی استفاده میکردیم و اگر میخواستیم مقدار اون رو تو متدی جز render مثل lifecycleهای کامپوننتمون استفاده کنیم دوشواری داشتیم و باید حرکت خاصی میزدیم حالا این ویژگی که به صورت static در دسترسه کارمون رو راحت تر کرده و میتونیم به شکلی که تو مثال میبینین به مقدار context دسترسی داشته باشیم و به راحتی تو جاهایی مثل lifecycleها ازش استفاده کنیم.

https://gist.github.com/theham3d/594542a90061eea05382a8fef6eaaa49
نکته : در این متد ما فقط میتونیم مقدار یک context رو اصطلاحا consume کنیم

متد getDerivedStateFromError

تو نسخه ۱۶ ری‌اکت بود که مفهوم Error Boundaries معرفی شد همراه با لایف سایکل componentDidCatch خوب شاید بگید این چه ربطی به این متد جدید داره اما در واقع این دوتا مکمل هم هستش . اساس کار getDerivedStateFromError برای هندل کردن Fallback UI هستش کاری که با cDC هم البته میتونیم انجام بدیم با کمک setState اما تفاوت این ۲ تا در زمان اجرا شدنشونه و این نکته هم باید در نظر داشت که این کار در نسخه های آینده ری‌اکت منسوخ میشه و میتونیم فقط برای لاگ کردم خطا هامون ازش استفاده کنیم .

اما تفاوت چیه : لایف سایکل cDC زمانی اجرا میشه که خطا اتفاق افتاده و بعد از اینکه DOM اپدیت شده و درست قبل از اجرا از متد render مقدار null برگردونده میشه و بعد ازون میتونیم Fallback UI رو نشون بدیم که این ممکنه باعث بوجود اومدن مشکل در کامپوننت های parent بشه. اما متد gDSFE در مرحله render اجرا میشه قبل از اینکه DOM به صورت کامل آپدیت بشه و در صورت اجرا یه object از خطا به متد پاس داده میشه و باید یه آبجک return بشه برای بروزرسانی state و نمایش Fallback UI درست جوری که ما از getDerivedStateFromProps میکنیم. بریم یه مثال بزنیم برای درک بهتر.

https://gist.github.com/theham3d/f0ee89e5f99c4ff8433d875d04936d52
نکته : این ویژکی هنوز تو SSR در دسترس نیست

خطاهای جدید StrictMode

یکی دیگه از چیزهای که تو نسخه ۱۶.۳ اضافه شده StrictMode بود که اگر در موردش آشنا نیستین میتونید اینجا در موردش بخونید . تو این نسخه جدید فقط ۲ مورد به لیست مواردی که در صورت استفاده باعث نشون دادن warning میشه اضافه شده که در زیر میتونید ببینید.

  • اولین مورد ReactDOM.findDOMNode که استفاده ازش در کل توصیه نمیشه ولی خوب اگر نمیدونید چیه و کنجکاو هستین بدونین اینجا رو بخونید .
  • دومین مورد هم Context Api قدیمی ری‌اکت

و اما نسخه ۱۶.۷ و Hook های که بکارتون میاد

۲۵ اکتبر بود که Dan Abramov در جریان ReactConf2018 ویژگی جدید ری‌اکت یعنی Hookها رو معرفی کرد که خیلی از کسایی که از React برای توسعه استفاده میکنند رو هیجان زده کرده از جمله خودم یجوری که کلا فضای توییتر رو Hook گرفته بود ((((:

نکته : ویژگی Hookها قراره که تو نسخه ۱۶.۷ اضافه بشه که هنوز به صورت نهایی release نشده پس اینجاست که باید گفت stay tuned for the next release، البته که میتونید اونارو الان تست کنید ولی در نسخه alpha در دسترس هستن . خب بریم سراغ اصل مطلب و معرف اونها.

اول از همه : اصلا این Hook چی هست ؟

اگر بخوام خیلی ساده بگم Hookها قراره چیز های رو به Functional Componentها اضافه کنن که قبل از این فقط در دسترس Class Componentها . بریم سراغ معرفیشون

هوک useState

یکی از ویژگی های مهمی که در صورت استفاده از Functional Componentها دیگه در دسترس ما نبود sateها بودن که با این هوک به راحتی میتونیم از این مفهوم در Functional Componentها استفاده کنیم حالا بریم سراغ مثال :

https://gist.github.com/theham3d/f040d218c46a5961fb37b3ffc3aa896d

ما با استفاده از Destructuring Assignment میتوانیم getter و setterه و stateای که تعریف کردیم را بگیریم و مقدار اون رو نمایش بدیم و یا اون رو تغییر بدیم .

نکته : ما به هر تعدادی که نیاز داریم میتونیم از useState استفاده کنیم و برای خودمون state تعریف کنیم

هوک useEffect

یکی دیگه از ویژگی های مهمی که تو Functional Componentها در دسترس ما نبود lifecycleها بودن، حالا ما میتونیم با استفاده از useEffect به این ویژگی دسترسی داشته باشیم .

ما از لایف سایکل های Class Component بیشتر وقتی استفاده میکردیم که میخواستیم کاری بکنیم که side-effect داشت مثل Fetch کردن داده ها و... این هوکم دقیقا برای همین ساخته شده که امکان انجام اینجور کارهارو تو FCها به ما میده همونجور که از اسمشم مشخصه

این هوک در ۳ زمان از چرخه حیات کامپوننت اجرا میشه :

  • بعد از اولین رندر که معادل componentDidMount هست
  • بعد از هر ری‌رندر که معادل componentDidUpdate هست
  • هنگام unmount شدن که معادل componentWillUnmount هست

خب بریم یه مثال بزنیم ، من میخوام واسه این که موضوع بهتر جا بیفته یه کامپوننت خیلی ساده رو که دقیقا عملکردشون یکیه رو تو ۲ حالت Class و Function بنویسم .

https://gist.github.com/theham3d/ded1ecb09fc5ea673594ed41ba7250b5

ازون جایی که useEffect بعد از اولین رندر و هر ری‌رندر اجرا میشه اگر بخوایم فقط در اولین رندر اجرا بشه یا فقط بعد از هر ری‌رندر داستان چیه ؟ useEffect به عنوان پارامتر دوم یه آرایه از مقادیر میگیره و باعث میشه فقط وقتی اون مقادیر تغییر کنن دوباره اجرا بشه . مثال های زیر رو ببینید.

اجر فقط در اولین رندر : معادل componentDidMount

برای اینکه کدهای داخل useEffect فقط موقع mount شدن اجرا بشه میتونید به آرایه خالی به عنوان پارامار دوم بهش بدیم.

// pass an empty array to useEffect as the second parameter
useEffect(() => {
    // your code
}, [ ]);

اجرا در صورت تغییر مقداری : معادل componentDidUpdate

// this will only run if count changes
useEffect(
    () => {
        // run here if count changes
     },
     [count]
);

خُب پس componentWillUnmount چی میشه، اگر بخوایم فقط موقع unmount شدن کاری رو بکنیم چی، برای اینکارم فقط کافیه یه function از داخلی هوک return کنیم که باعث میشه کدهای اون موقع unmount شدن اجرا بشه.

useEffect(() => {
    Api.subscribeSomething();
    // equal to componentWillUnmount
    return () => {
        Api.unsubscribeSomething();
     };
});
نکته : دقیقا مثل useState ما از useEffect هم میتونیم به دفعات استفاده کنیم
خُب کلام آخر و مهم درباره useEffect اینه که این هوک برخلاف لایف سایکل ها cDM و cDU مرورگرتون رو بلاک نمیکنه که این تو response اپتون خیلی کمک میکنه اما نقطه مقابل useEffect هوکی به نام useLayoutEffect هست که عملکردی شبیه به cDM و cDU داره و نحوه استفاده اون هم دقیقا مثل useEffect هست
ارجاع : یه هوک دیگه هم داریم به نام useMutationEffect که نحوه استفادش شبیه به useEffect هست که زمانی که اجرا میشه متفاوته که خودتون میتونید برید در مورد این هوک و تفاوتش با useLayoutEffect مطالعه کنید.

هوک useContext

عملکردش دقیقا شبیه متد contextTypeی هست که بالا در موردش صحبت کردیم پس در موردش دیگه توضیح نمیدم و فقط به ساده در موردش میزنم.

https://gist.github.com/theham3d/2ca027ebd4302e98627c9cecd9b7bc3c

هوک useMemo

کار این هوک اینه که یک مقدار memoized شده برمیگردونه به این معنی که اگر ما تابعی داشته باشیم که اجرا کردن اون cost زیادی برای ما داشته باشی این هوک با گرفتم اون تابع و لیست پارامترها فقط و فقط زمانی اون تابع رو اجرا میکنه که مقدار داده های تغییر کرده باشه و آخرین مقدار رو همراه با پارامتر ها در خودش کش میکنه .

اگر در مورد Memoization اطلاعی ندارید خیلی توصیه میکنم این لینک رو بخونید.

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

یه هوک دیگه هم داریم به نام useCallback که دقیقا عملردی شبیه به useMemo داره و یه ورژن memoized شده از callback رو بر میکردونه که میتونید در موردش تو داکیومنت ری‌اکت بخونید و من دیگه در موردش توضیح نمیدم.

هوک useReducer

اگر با Redux آشنا باشید که به احتمال زیاد هستین روش کاری این هوک براتون جدید به نظر نمیرسه و توضیح زیادی نمیدم فقط در همین حد که عملکردش شبیه useState هست با این تفاوت که علاوه بر مقدار اوله ما میتونیم یه reducer هم بهش بدیم و بسته به actionای که dispatch میکنیم مقدار stateمون رو تغییر بدیم، به همین راحتی به همین خوشمزگی .

حالا همون Counter بالا رو اینبار با useReducer بنویسیم.

https://gist.github.com/theham3d/9c761239120b480d92d56b9ab43f7ba8
نکته : به این هوک یک پارامتر سومی هم میتونید بدید به عنوان initialAction که وقت رندر شدن اولیه به صورت اتوماتیک dispatch بشه

قوانین استفاده از هوک ها

برای استفاده از هوک ها فقط کافیه که ۲ تا قانون زیر رو رعایت کنید :

  • استفاده در top-level و استفاده نکردن در شرط ها ، حلقه ها و nested functionها
  • استفاده از اونها فقط در Function Componentهای ری‌اکت نه تابع های معمولی جاوااسکریپت

خود تیم ری‌اکت هم یه پلاگین ESlint نوشته که به اسم eslint-plugin-react-hooks که خیالتون از این بابت هم راحت باشه

نکته : از توضیح در مورد ۲ هوک useRef و useImperativeMethods چشم پوشی کردم اما اگر در مودشون کنجکاوید میتونید در مورد اونها تو داکیومنت ری‌اکت بخونید

کلام آخر

در آخر ممنون که وقت گذاشتید و این پست رو خوندید، امیدوارم از محتوای این پست لذت برده باشین و به کارتون بیاد، تمام تلاشمو کردن متن خیلی خسته کننده نباشه و مختصر و مفید باشه، امیدوارم که اینطور بوده باشه .

و من الله التوفیق