خداحافظ ریداکس، سلام کانتکست (قسمت اول)

تو این مقاله یاد میگیرم که ابزارهای قدیمی رو دور بریزیم و به ابزارهای مدرن و جدید (و صد البته هزاران بار ساده تر) روی بیاریم. (اگر از گوشی موبایل دارید این مقاله می‌خونید و لینک رو از تلگرام باز کردید، مطمئن بشید که تو حالت instant view نیستید چون قسمت‌هایی از مقاله تو اون حالت نمایش داده نمیشه)

پیشنهاد می‌کنم که اول این مقاله رو مطالعه کنید تا متوجه بشید چرا ما به ریداکس نیاز داشتیم:

https://virgool.io/iran-react-community/%DB%B8-%D9%82%D8%AF%D9%85-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%AD%D8%B1%D9%81%D9%87%D8%A7%DB%8C%DB%8C-%D8%B4%D8%AF%D9%86-%D8%AF%D8%B1-react-%D9%82%D8%B3%D9%85%D8%AA-%D8%B3%D9%88%D9%85-gsssmznyjdun

همونطوری که داخل مقاله بالا بهش اشاره کردم، ما برای ذخیره و جابه‌جا کردن اطلاعات بین کامپوننت‌ها به ابزار Global state management نیاز داریم. تو اکو سیستم ری‌اکت برنامه نویسا معمولا از ابزاری به نام ریداکس استفاده می‌کنن تا اطلاعات رو بین کامپوننت‌ها جابه‌جا کنن.
یادگیری ریداکس خیلی سخته، از طرفی خیلی خیلی خیلی پیچیده هستش. خیلیییییی


چی شد که همه رو آوردن به ریداکس

ما از روز اول کانتکست رو نیاز داشتیم ولی چون تو نسخه‌های اولیه ری‌اکت کلا کانتکست نبود و بعدشم Legacy Context اومد که خیلی سخت و عجیب قریب بود پس همه رفتن سمت Global State Management ها.

بعدا که نسخه ۱۶.۳ ری‌اکت اومد کانتکست معرفی شد ولی امکانات کافی نداشت که جایگزین Global State Management ها بشه و دوباره این سنت دیرینه که هر پروژه ری‌اکت حتما باید ریداکس داشته باشه با ما برنامه نویسای ری‌اکت موند. قبل از اینکه ادامه تاریخچه کانتکست رو بگم، نیازه یه معرفی اولیه داشته باشیم که با نحوه استفاده از کانتکس هم آشنا بشید.

خب کار با کانتکس خیلی ساده هستش، چون کانتکست هیچ چیز جدیدی نیست به جز استیت یه کامپوننت دیگه. شما فقط کافیه با ری‌اکت آشنا باشید تا بتونید کانتکست رو تو ۵ دقیقه یادبگیرید.

به صورت کلی کانتکست که یه جور تونل هستش که میشه اطلاعات رو با اون بین کامپوننت‌ها جابه‌جا کرد و هروقت دیتا موجود داخل Provider عوض بشه کامپوننت‌های متصل به اون تونل دوباره رندر میشن ...

خب این Provider که بالا اسمشو آوردیم چی هستش و از کجا میاد؟
برای دسترسی به Provider کافیه متد createContext رو صدا بزنید تا یک شی Context داشته باشید

کامپوننت Provider که بالا درباره اون صحبت کردیم داخل آبجکت MyContext هستش، این کامپوننت یه پراپرتی (همون prop) داره به نام value که باید مقدار دهی بشه. هر مقداری داخل value بزارید به تمام کامپوننت‌هایی که به این کانتکس وصل هستن ارسال میشه

تو مثال بالا ما یه کامپوننت ساختیم به نام FelanProvider (اسم کامپوننت زیاد مهم نیست که چی باشه ولی خب واسه خوانا بودن کد بهتره آخرش با Provider تموم بشه)، اگر دقت کرده باشید داخل متد رندر ما MyContext.Provider رو رندر کردیم و مقدار count که داخل استیت بود رو داخل پراپ value اون قرار دادیم. این یعنی الان داخل Context ما عدد ۰ هستش (چون مقدار اولیه استیت ما ۰ هستش).

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

this.setState({ count: 2 })

حالا اگر کامپوننت‌هایی که به این کانتکست متصل هستن بخوان استیت کانتکست رو عوض کنن باید چی‌کار کنن ؟ خب با حرفایی که ۲ خط بالاتر زدیم معلوم شد که باید setState بکنیم، ولی کدوم setState ؟ اون کامپوننت که به کانتکست وصل هستش استیت خودشو داره و setState خودشم داره پس اونو نباید صدا بزنیم !
واسه همین میایم متد setState کامپوننت FelanProvider رو هم داخل کانتکس قرار میدیم.

بدترین روش برای قرار دادن setState داخل کانتکست :

اینکه چرا بدترین روش هستش رو پایی تر توضیح میدم
اینکه چرا بدترین روش هستش رو پایی تر توضیح میدم

روش جایگزین :

این روش خیلی بهتره ولی خوانایی کافی رو نداره از طرفی باعث میشه کد ما یک پارچه نباشه، به این مسئله فکر کنید که اگر به جای کلاس کامپوننت فاکنشن کامپوننت بود setState رو کجا می‌خواستیم قرار بدیم ؟
این روش خیلی بهتره ولی خوانایی کافی رو نداره از طرفی باعث میشه کد ما یک پارچه نباشه، به این مسئله فکر کنید که اگر به جای کلاس کامپوننت فاکنشن کامپوننت بود setState رو کجا می‌خواستیم قرار بدیم ؟


بهترین روش:

تیم فیسبوک اعلام کرده که کلا از این روش برای قرار دادن setState استیت استفاده می‌کنه
تیم فیسبوک اعلام کرده که کلا از این روش برای قرار دادن setState استیت استفاده می‌کنه

داخل مثال بالا ما ۲ تا کانتکس ساختیم، داخل اولی setState رو گذاشتیم و داخل دومی state رو قرار دادیم. این روش خیلی optimize هستش (پایین تر توضیح میدم درباره این موضوع)

معادل کد بالا رو بخوایم با فانکشن کامپوننت و هوک بنویسیم میشه:

اینکه Provider ما فانکشن کامپوننت باشه یا کلاس کامپوننت هیچ اهمیتی نداره (صرفا یک مثال ساده هستش برای جا افتادن مسئله)
اینکه Provider ما فانکشن کامپوننت باشه یا کلاس کامپوننت هیچ اهمیتی نداره (صرفا یک مثال ساده هستش برای جا افتادن مسئله)

اگر با هوک آشنایی ندارید مقاله و آموزش ویدیویی من درباره هوک رو از لینک زیر دریافت کنید:

https://virgool.io/iran-react-community/react-hooks-gnhzymcx21sf

برگردیم به تاریخچه ...
گفتم که تو نسخه ۱۶.۳ کانتکست امکانات کافی رو نداشت! منظورم چی بود؟ خب ما تا اینجا فقط اطلاعات رو داخل تونل قرار دادیم، برای خوندن اطلاعات بیاید ۲ تا کار کنیم.

اول اینکه کامپوننت FelanProvider رو باید یه جایی رندر کنیم (ترجیحا یه جایی بالای درخت ری‌اکت باشه همونجایی که Provider ریداکس رو رندر می‌کردید):

اگر از Create React App استفاده می‌کنید، فایل App.js رو برای اینکار انتخاب کنید
اگر از Create React App استفاده می‌کنید، فایل App.js رو برای اینکار انتخاب کنید

واسه اینکه به دیتای Provider دسترسی پیدا کنیم کافیه که Context رو ایمپورت کنیم و اونو رندر کنیم:

صرفا جهت مثال زدن داخل قطعه کد بالا ۲ مدل کامپوننت رو قرار دادم
صرفا جهت مثال زدن داخل قطعه کد بالا ۲ مدل کامپوننت رو قرار دادم

مشکل این نسخه از ری‌اکت و کانتکست اینکه شما داخل لایف سایکل‌ها به دیتای کانتکست دسترسی ندارید (البته یه راه هستش به نام Higher Order Component گذاشتن ولی یکم کثیف کاری داره) و اینم بگم تو اون زمان اصلا هوک وجود نداشت پس فانکشن کامپوننت‌ها زیاد مشکلی نداشتن.

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

https://virgool.io/iran-react-community/%DB%B8-%D9%82%D8%AF%D9%85-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%AD%D8%B1%D9%81%D9%87%D8%A7%DB%8C%DB%8C-%D8%B4%D8%AF%D9%86-%D8%AF%D8%B1-react-%D9%82%D8%B3%D9%85%D8%AA-%D8%B3%D9%88%D9%85-gsssmznyjdun

تا اینجا شما یاد گرفتید که چه‌طوری کانتکست رو راه اندازی کنید ولی خب استفاده از کانتکست اونقدرم دلنشین نبود (چی بود اون consumer گذاشتن و render prop کردن، حال آدم بد میشد اصلا)

خلاصه چند وقت از منتشر شدن نسخه ۱۶.۳ گذشت و رسیدیم به نسخه ۱۶.۶، تو این نسخه شما با استفاده از static contextType می‌تونستید به اطلاعات کانتکست راحت تر دسترسی پیدا کنید

خب نسبت به نسخه قبلی خیلی بهتر شد و میشه به کانتکس مثل استیت یا پراپ رفتار کرد (حتی داخل لایف سایکل هم بهش دسترسی دارید) ولی متاسفانه فقط میشه یک کانتکس رو به این کامپوننت متصل کرد و این چیزی نیست که بشه باهاش کنار اومد چون تو پروژه‌های واقعی (منظورم اینکه تو مثال میشه یه جوری سر و تهش رو هم آورد ولی تو واقعیت نمیشه) به مشکل می‌خوریم...


ری‌اکت ۱۶.۸ و ظهور هوک (Hook)

قبل از هرچیزی باید بگم اگر نمی‌دونید هوک چه‌طوری کار می‌کنه حتما ویدیو آموزش و مقاله من رو درباره هوک‌هارو مطالعه کنید:

https://virgool.io/iran-react-community/react-hooks-gnhzymcx21sf
const data = React.useContext(CONTEXT_OBJECT)


داخل اون ویدیو درباره کاستوم هوک صحبت کردم پس دیگه اینجا توضیح زیادی درباره کاستوم هوک نمی‌دم. کاستوم هوک‌ها همون فانکشن‌هایی هستن که همیشه ازشون استفاده می‌کردیم ولی داخلشون از هوک‌های ری‌اکت استفاده می‌کنیم پس باید قوانین هوک‌ها رو رعایت کنن، از طرفی به صورت قرار دادی واسه راحت بودن تشخیص کاستوم هوک‌ها از فانکشن‌ها پیشنهاد میشه نام کاستوم هوک‌ها با use شروع بشه، برای مثال بالا میشه نام useFelan رو به عنوان نام کاستوم هوک قرار داد.

import React from &quotreact&quot
import { MyContext } from &quot../Providers/FleanProvider&quot

function useFelan() {
  return React.useContext(MyContext)
}

export default useFelan
 خیلی کوتاه و ساده و خوانا ?
خیلی کوتاه و ساده و خوانا ?

حالا شما حالتی رو در نظر بگیرید که یک کانتکست برای ذخیره کردن اطلاعات کاربر داشته باشید به نام UserProvider و نام کاستوم‌ هوک اون رو useUser قرار داده باشید. (و یا سبدخرید که میشه CartProvider و useCart) و دسترسی به اون خیلی راحت هستش فقط کافیه هوک رو ایمپورت کنید و فراخوانی کنید.
برای مثال کامپوننت Private Route که برای React Router نوشتید به صورت زیر پیاده سازی میشه:

اگر آموزش React Router رو نیاز دارید پیشنهاد می‌کنم دوره رایگان آموزش React Router 5 من رو ببینید.


فکر نکنم کسی تو ایران باشه که از خوبی‌های ماشین پیکان حرفی نزنه :)
پیکان با این که ماشین خوبی بود کولر نداشت و با گذشت زمان قدیمی شد و از رده خارج شد، و جای پیکان رو ماشین‌های دیگه گرفتن

اگر نسخه‌های قدیمی ری‌اکت رو همون پیکان درنظر بگیریم و ریداکس رو به عنوان کولر، تا الان ما داشتیم یه همچین کاری می‌کردیم =)

خیلی فکر کردم یه مثال خوب پیدا کنم و این بهترین گزینه بود
خیلی فکر کردم یه مثال خوب پیدا کنم و این بهترین گزینه بود

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

- شاید الان پیش خودتون بگید ریداکس خیلی خفنه چون کلی middle-ware داره که میشه باهاش دیتا فچ کرد (redux-thunk) و یا دیتا رو persist کرد (redux-persist) و یا استیت رو بین تب‌های مروگر سینک کرد و ...

+ همه اینارو میشه با کانتکس‌هم پیاده سازی کرد، برای مثال persist شدن دیتا نهایتا ۵ خط کد نیاز داره :)، واسه دیتا فچ کردن نیازی به میدل ور ندارید و واسه سینک شدن اطلاعات بین تب‌های مرورگر کافیه از broadcast api استفاده کنید.

- یا شاید پیش خودتون بگید مدیریت استیت بدون reducer خیلی سخت میشه !

+ باید بگم تیم سازنده ری‌اکت دقیقا همون کد reducer که داخل ریداکس هستش رو کپی کردن داخل هوک useReducer

- ریداکس action creator داره، اینو چی میگی آقای نویسنده

+ کانتکست هم می‌تونه action creator داشته باشه هم می‌تونه نداشته باشه بستگی داره به کاری که می‌خوای انجام بدی.

https://media.giphy.com/media/SKpJRAh1EggQ8/giphy.gif

- ولی ما قوم بنی اسرائیلیم هنوزم از ریداکس استفاده می‌کنیم
+ =)

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



ادامه مطالب رو سعی می کنم تا چند روز آینده در قالب مقاله دیگه‌ایی تکمیل کنم و لینکش رو همینجا قرار بدم. می‌تونید وب سایتی که فقط و فقط با استفاده از کانتکست نوشتم رو از آدرس staging.antivirus.ir ببینید و چند تا صفحه باز کنید و توی یکی از صفحه‌ها یک محصول به سبدخرید اضافه کنید و ببینید تو صفحات دیگه چه اتفاقی رخ میده.

http://vrgl.ir/dV8UZ


تا زمان نوشته شدن قسمت دوم این چندتا لینک رو مطالعه کنید:

https://reactjs.org/docs/context.html
https://reactjs.org/docs/hooks-reference.html#usereducer
https://kentcdodds.com/blog/how-to-use-react-context-effectively
https://kentcdodds.com/blog/how-to-optimize-your-context-value

خوشحال میشم بدونم این مقاله مفید واقع شده یا نه! و آیا شما شروع به استفاده از کانتکست به جای ریداکس می‌کنید یا نه ؟