تیم ری‌اکت دارن چیکار می‌کنن؟ (قسمت هفتم)

خب. ماجرای این قسمت برمیگرده به بحث Flare.

اگه حوصله خوندن ندارید اسکرول کنید پایین تا عبارت سومین PR رو ببینید.

و اگه نمی‌دونید Flare چیه یه نگاهی به دقیقه ۵۵ این ویدیو بندازید ( ویدیو از ارائه من در دورهمی جامعه ری‌اکت ایران)

با توجه به چیزی که توی این ویدیو گفتم تیم ری‌اکت مشغول تغییر و تحولات بنیادی تو بخش Event System شون هستن که اسم رمز Flare رو بهش دادن. و اونموقع انتظار داشتیم که خروجیی شبیه به این داشته باشه:

نحوه استفاده از Flare در ورژن های قدیمی
نحوه استفاده از Flare در ورژن های قدیمی

همونطور که توی دورهمی هم بشدت تاکید کردم که این API ها شدیدا expreimental هستن و هر لحظه ممکنه عوض بشن (حتی با وجود اینکه یه دموی قابل استفاده هم توی دورهمی ران کردیم و کار کردنش رو دیدیم)، الان توی این PR های جدید شاهد زیر و رو شدن API سیستم Flare هستیم.

قبل از هرچیز چندساعت قبل اینکه دورهمی ری‌اکت برگزار بشه خود Dominic یه PR رو داد که یه هوک برای Event ها اضافه می‌کرد.(توی ویدیوی بالا راجبش صحبت کردیم) یچیزی شبیه به این:

استفاده از hook های جدید Flare
استفاده از hook های جدید Flare

و یا حتی:

ویژگی propagate در hook های جدید Flare
ویژگی propagate در hook های جدید Flare

ولی متاسفانه تو این PR های جدید شاهد تغییر و تحول این API هستیم.

سوال اساسی اینه که چرا؟ مشکل این بود که API قبلی کاری که ری‌اکت میکرد رو واقعا نشون نمی‌داد و باعث میشد مردم فرضیات اشتباهی از قضیه داشته باشن. (یه مشکل کوچیکتر هم این بود که تو معماری بالا برای هر event component یه fiber ساخته میشه. که خب اونقدرا مهم نیست)

در واقع بین هر EventComponent و Host node (بخوانید کامپوننت Press و div) باید یک تناظر وجود داشته باشه (نه الزاما یک به یک). به این معنا که هر Event Component در طول زندگی خودش باید باید باید متعلق به یک و فقط یک HostNode باشه. حالا سوال پیش میاد که چی میشه که این قانون نقض میشه؟ مثال زیر رو ببینید:

نکته: useToggle و useInterval دوتا customHook هستن که فرض می‌کنیم خودمون جای دیگه پیاده‌شون کردیم
نکته: useToggle و useInterval دوتا customHook هستن که فرض می‌کنیم خودمون جای دیگه پیاده‌شون کردیم

توی این مثال وقتی که Press ساخته میشه کامپوننت div بعنوان اولین فرزندش بهش داده میشه. بعد از یک ثانیه بدون اینکه خود Press رو دوباره بسازیم یا ری‌رندر کنیم کامپوننت فرزندش رو تغییر میدیم و اینبار یه button بجاش میذاریم. اما متاسفانه Press ما همچنان به همون div اولیه وصل شده و API هم خیلی راحت بهمون اجازه میده همچین bug هایی بوجود بیاریم.

از طرفی هم کامپوننتی مثل FocusScope میتونه چندتا فرزند بگیره اما Focus فقط یک فرزند میپذیره و این عدم یک‌دستی در API رو بوجود میاره.

حالا تیم ری‌اکت شروع کردن که تو این حالت ها بهمون warning نشون بدن. ولی پیدا کردن و نمایش دادن این warning ها نسبتا مشکل بود. (این if ساده ترین حالت ممکنه. مثلا حالتی رو فرض کنید که کامپوننت فرزند Suspense شده)

برای همین (و یسری مورد دیگه) تیم به فکر API جایگزین افتادن.

تو این مقاله سه تا PR مختلف رو به ترتیب بررسی می‌کنیم که ببینیم چه تغییر و تحولاتی رخ داده.

اولین PR:

اول Dominic طی این PR یه شکل جایگزین رو بررسی کرد. با این سیستم جدید کدهامون اینشکلی میشن:

استفاده از ref در API جدید
استفاده از ref در API جدید

توی این راه‌حل جایگزین EventResponder ها مستقیما به ref وصل می‌شن. اینجوری دیگه مشکلاتی که بالاتر ذکر کردیم بوجود نمیاد. هر Responder به یک ref وصل میشه و اگر تغییری اتفاق بیافته pressResponder هم بی‌اطلاع نمی‌مونه.

اما این PR هیچوقت مرج نشد و همونطور که Dominic گفت طی بحث های داخلی به یک API جایگزین رسیدن که میشه موضوع PR بعدی.

دومین PR:

این PR نتیجه صحبت های داخلی تیم بود. از اونجایی که خیلی وقتا استفاده از ref می‌تونه منجر به باگ بشه(برای ما نه، وقتی که برای ری‌اکت کد میزنیم منظورمه) طی این PR اومدن event listener ها رو بجای اولین host node فرزند، به اولین host node پدر چسبوندن و بیخیال ref ها شدن.

بشخصه خیلی خوشحالم که API نهایی این نبود (قطعا متخصصین امر API نویسی تو تیم نمیذارن این به production برسه و صرفا یه آزمایش کوچولو بود که مام نباید خیلی باخبر میشدیم :دی) اما درنهایت یه همچین چیزی به دست ما میرسه:

هر EventResponder بجای اولین host node فرزند به اولین host node پدر میچسبه
هر EventResponder بجای اولین host node فرزند به اولین host node پدر میچسبه

توی این طراحی جدید دیگه (خوشبختانه) نیازی به ref نداریم. و ازونجایی که children یکی از prop های کامپوننت div محسوب میشه مشکلی با حالت های conditional نخواهیم داشت. و حتی اگه کاربر با کلی زحمت موفق بشه همچین باگی ایجاد کنه ری‌اکت خیلی راحت متوجه میشه و warning نشون میده.

درباره این PR کلی بحث شکل گرفت و واضحا خودشون هم از این API رو به بالا راضی نیستن. ولی خب بهترین گزینه در لحظه بوده که ارزش چک کردن داشته.

سباستین خیلی از این سیستم جدید خوشش میاد و معتقده خیلی خوب با معماری ری‌اکت هماهنگه و مناسبه کارشون هست. توی این کامنت یسری دلیل رو ذکر می‌کنه اما مهمترینشون اینه که ری‌اکت (و همه فریم‌ورک های فرانت‌اند) خیلی روی component هاش مانور میده. توی سیستم قبلی خیلی راحت بود که از بیرون کامپوننت بتونیم کامپوننت داخلی رو خراب کنیم. یا مجبور باشیم از ساختار کامپوننت داخلی اطلاع داشته باشیم تا بتونیم از EventComponent ها استفاده کنیم (همون مثال کامپوننت های A, B که بالاتر زدم.)

اما سیستم جدید اینطور نیست. سباستین معتقده که مشکل مشابهی هم با ref ها داشتن. بعنوان مثال اول میخواستن خودکار forwardRef هارو بچسبونن به بیرونی‌ترین hostNode یه کامپوننت فرزند. یعنی بعنوان مثال اگه همچین چیزی داشته باشیم:

مشکلات auto forward ref
مشکلات auto forward ref

سیستم forwardRef اتوماتیک برای MyCustomInput1 خیلی خوب کار می‌کنه ولی اگه یه div (مثلا برای استایل دهی) به دور input مون اضافه کنیم (مثل MyCustomInput2) دیگه forwardRef از کار میافته. و ایرادهایی از این جنس بود که باعث شد کاری کنن فانکشن forwardRef قدرت بیشتری بهمون بده.

حالا همین مشکل ها هم برای API قبلی صادق بود. بعنوان مثال شاید بیرونی ترین host node یه کامپوننت مناسب ترین جا برای چسبوندن event listener ای از جنس Hover نباشه.

اما با همه این تفاسیر سباستین معتقده که این API یکم گنگ و نامفهومه و یسری موضوع پیشنهاد میده برای بهتر کردنش:

۱- یه کوچولو تر و تمیز کردنش یا بهبود های ریز syntax ای. یچی تو مایه های این مثلا:

تبدیل component ها به function
تبدیل component ها به function

که خب واقعا زحمت کشید با این پیشنهادش :) خسته شد.

۲- بیان این کانسپت listener هایی که دارن رو وااااااااااااااقعا بعنوان یه هویت جدید درنظر بگیرن و بصورت first class ازش پشتیبانی کنن. حتی توی خود jsx. پیشنهادش اینه که میتونیم یه کانسپت جدیدی توی jsx معرفی کنیم به عنوان decorator برای تگ ها. که این Responder ها یه مدل decorator باشن

۳- بجای اینکه خود کد رو First class کنن بیان کانسپت رو first class کنن. میگه که اگه چیزهای دیگه‌ای هم پیدا کنیم که API شبیه به این داشته باشن میتونیم همرو باهم معرفی کنیم و یاد دادنش کلی راحت تر میشه. با تغییر دادن نحوه تفکر مردم و دیدشون نسبت به jsx میتونن راحت تر با این API کنار بیان. میگه که اگه بهش به شکل چیزی که داخل dom رندر میشه نگاه کنی، خیلی عجیب و دور از ذهنه که چجوری میتونه روی parent خودش listener بذاره، ولی اگه بهش به چشم یه ابزار طراحی visual نگاه کنی کار راحت تر میشه. مثل ابزار fill توی paint که وقتی توی شکل کلیک می‌کنی پدرش رو پر می‌کنه، این هم یچیز تو این مایه‌هاس

بگذریم. درنهایت هرچی که بود این هم مرج نشد و رفتیم سراغ API آخر.

سومین PR:

اینیکی دیگه مرج شده :)

این PR دیزاینی رو معرفی می‌کنه که هم مشکلات رو حل می‌کنه هم API تمیزتری نسبت به PR دوم داره.

سیستم جدید از دو بخش کلیدی تشکیل میشه، Responder ها و Listener ها.

طبق این API جدید دیگه بجای اینکه Responder هارو بعنوان بچه های کامپوننت بدیم، باید بعنوان prop پاس داده بشن. چیزی شبیه به این:

آخرین API پیشنهاد شده برای Flare
آخرین API پیشنهاد شده برای Flare

بنظر من که nice میاد ?

هر host node (بخونید div) میتونه هرچندتا که دلش خواست responder ورودی بگیره و وقتی گرفت یه اتصال بین responder ها و این div رخ میده که کاملا واضحه. دیگه ممکن نیست مشکل conditional ندانسته بوجود بیاد.

و functional component ها می‌تونن به این شکل Listener بشن:

نحوه تعریف listener
نحوه تعریف listener

هروقت event ای روی هرکدوم از EventResponder ها رخ بده، شروع به propagate کردن در درخت ری‌اکت می‌کنه تا زمانی که به اولین listener برسه. اونجا listener اون event رو هندل می‌کنه و متوقف میشه.

اگر هم چندتا Responder کنار همدیگه داشته باشیم، Listener به همشون گوش می‌کنه:

چند div با PressResponder
چند div با PressResponder

تو این مثال PressListener داخل App هم به Press های Div A گوش میده هم Div B.

در نهایت برای مثالی که اول مقاله داشتیم، توی سیستم جدید به اینشکل در‌اومده.

قبل عمل:

بعد عمل:

والسلام :)

شب خوش.

http://vrgl.ir/plcbz

دیگر مقالات من:

https://virgool.io/iran-react-community/%DA%A9%D8%A7%D8%B1%D9%87%D8%A7%DB%8C-%D8%AA%DA%A9%D8%B1%D8%A7%D8%B1%DB%8C-%D9%85%D9%85%D9%86%D9%88%D8%B9-%D9%82%D8%B3%D9%85%D8%AA-%DB%B1-numfwzclbnqj
https://virgool.io/JavaScript8/%D9%81%D8%A7%D9%86%DA%A9%D8%B4%D9%86%D8%A7%D9%84-js-%D8%A8%D8%AF%D9%88%D9%86-%D8%AF%D8%B1%D8%AF-%D9%88-%D8%AE%D9%88%D9%86%D8%B1%DB%8C%D8%B2%DB%8C-%D8%A8%D8%AE%D8%B4-%DB%8C%DA%A9-reduce-tghl6fzl5r8u
https://virgool.io/iran-react-community/how-to-paginate-in-reactredux-like-a-hero-y0puyffsdj6v