آیا از نوشتن .catch های فراوان برای گیر انداختن ارور ها خسته شدین؟
آیا از نوشتن پشت سر هم headers و url طولانی api ها خسته شدین؟؟
آیا از نداشتن یه ایده درست درمون برای کنسل کردن ریکوئست ها در useEffect (موقع unmount شدن) خسته شدین؟؟؟
آیا از نداشتن یه استراکچر تر تمیز و مرتب برای fetch request ها خسته شدین؟؟؟؟
کتابخونه دوست داشتنی AXIOS اینجاست تا خستگی رو از تنتون در بیاره?
با این کتابخونه دیگه نیازی به استفاده از fetch برای ریکوست ها نیست و همه ی مشکلاتی که بالاتر عنوان کردم رو میتونیم به راحتی از سر راه برداریم.
توی این مقاله قدم به قدم یه استراکچر خفن با AXIOS برای ریکوئست هامون ایجاد میکنیم و از دوباره نویسی های زیادی جلوگیری میکنیم و حین پیاده کردن این استراکچر خط به خط توضیح میدم که چرا و چگونه ؟
یه جورایی سبک این پست مثل "یادگیری ریداکس، ساده تراز چیزی که فکر میکنی!" هستش.
توی مثال بالا که از fetch استفاده شده ما سه تا ریکوست داریم، حالا اگه بخوایم مشکلات مثال بالا رو نام ببریم اینه که : 1. urlها توی همشون قسمت عظیمیشون تکراری هست ( baseURL ) و فقط تکرار شده. 2. headers توی همشون تکرار شده... 3. بلایی که میخوایم سر ارور بیاریم رو مجبوریم تک تک روی هر ریکوئست با .catch بنویسیم
اینجاس که میریم سراغ AXIOS
npm install axios یا yarn add axios
پوشه services شامل سه تا پوشه هست:
1.پوشه config دارای یه فایل index.js هست که کانفیگ اولیه رو توش میزاریم و اون تکرار کردن های بالارو تا حدی برطرف میکنیم توش
ببینید ما درسته axios رو نصب کردیم اما اگه ازش به طور معمولی و بدون کانفیگ بخوایم استفاده کنیم اصلا فرق خاصی با همون fetch نخواهد داشت، برای مثال:
اگه مثال بالارو نگاه کنید میبینید که AXIOS بدون کانفیگ همون تکرار کردن هارو داره...
حالا چیزی که توی فایل index پوشه ی config قرار میدیم :
کاری که کردیم اینه که ما با متد create یه نمونه از axios ساختیم به اسم api (هر اسمی میتونید بدید بجاش) و این متد یه ابجکت به عنوان پارامتر میگیره که تمام کانفیگ رو داخلش میتونیم قرار بدیم، ینی چی؟ ینی اینکه هرچی اینجا قرار بدیم از این به بعد توی همه ی ریکوئست هایی که با api میزنیم به طور خودکار صدا میشه و نیازی نیست برای تک تک ریکوئست ها هدر و base url و ... قرار بدیم?
به این شکل:
توی عکس بالا اون ریکوئست های بدون کانفیگ کامنت شدن و بجاشون از نمونه ای که خودمون ساختیم استفاده کردیم و دقیقا همون کارهارو انجام میدن اما خیلی تر تمیز و شیک تر ? زیبا نیست؟؟؟
نکته درباره مثال بالا: توی متد put و post چیزی که به عنوان پارامتر دوم داریم میفرستیم در اصل همون body هستش و نمیشه که به صورت کانفیگ قرارش بدیم چون به هرحال هر ریکوئست بادی مخصوص خودشو نیاز داره. مثلا توی مثال بالا متد پست داره ایمیل و پسورد رو به عنوان بادی میفرسته تا لاگین کنه
نکته : base url چیزیه که به اول url هامون اساین میشه مثلا اگه ما بگیم api('/users') اون base url میاد به قبلِ users/ اضافه میشه.
نکته دوم: headers برای اینه که توکنی که موقع ثبت نام گرفتیم رو به ریکوئست هامون اساین کنیم تا بتونیم از قابلیت های api استفاده کنیم.
نکته سوم: ما برای متد های مختلف ریکوئست مثل post , get , put , delete فقط کافیه از axios به این شکل بگیریمشون: api.post('/register') , api.put('/login') و... البته متد get دیفالت هست و در صورت مشخص نکردن متد برای ریکوئست get به طور پیش فرض انتخاب میشه : api('/users')
پس ما از این به بعد بجای axios از api استفاده میکنیم چون الان api یه نمونه از axios هست فقط با کمی کانفیگ درست درمون برای راحتی کار و ما قراره اینو توی دوتا پوشه های همسایه اش ایمپورت و استفاده کنیم.
2.پوشه ErrorHandler پوشه ایه که ما با اضافه کردن یه قابلیت به نمونه api خودمون ، از شر .catch های مکرر تا حدودی خلاص میشیم.
توی این پوشه یه فایل index.jsx قرار داره که شامل کد زیره:
بله! این فایل یه کامپوننته، اما اگه خط آخر رو نگاه کنید میبینید که این کامپوننت هیچی برنمیگردونه و تو خالیه و تنها چیزی که توش بدردمون میخوره همون فانکشن بالاشه که قراره کالبدشکافیش کنیم ( البته این error handling با axios رو میشد به شکلای دیگه هم در آورد ولی خب من روش خودمو که همین باشه توضیح میدم.
یه سری نکات مفید که قبل از کالبدشکافی کد بالا اگه با وضعیت های مختلف ارور ها آشنا نیستید بهتره بدونید:
ارور با وضعیت 400: این برای وضعیتیه که ریکوئست رو بد زدیم حالا ممکنه یه کلمه رو اشتباه زده باشه توی url یا توی بادی دیتا رو بد فرستاده باشیم و....
ارور با وضعیت 401: ما برای استفاده از api به یه توکن نیاز داریم که این توکن رو کاربر وقتی ثبت نام یا لاگین میکنه میگیره و ست میشه توی همون کانفیگی که بالاتر دیدیم، حالا این توکن ها یه زمان انقضا دارن که وقتی فرا برسه دیگه کار نمیکنه و اونجاس که با این خطای 401 مواجه میشیم و کاربر باید دوباره لاگین کنه تا توکن جدید بگیره یا ام که واسه برخورد نکردن با این ارور باید قبل از زمان انقضا با رفرش توکن بیایم توکن رو زمان انقضاشرو تازه کنیم ( که خود رفرش توکن یه مبحث جدا و مفصله...)
ارور 403: این هم برای وضعیتیه که کاربر ممکنه حتی با داشتن توکن نتونه به یه سری چیزا دسترسی داشته باشه و ازشون استفاده کنه که اونوقت این ارور ظاهر میشه و مثلا میگه شما اجازه استفاده از این قابلیت رو ندارید.
ارور 404: وقتی میخوایم یه دیتایی رو از سرور بگیریم که پاک شده (مثلا یه بلاگ از فلان کاربر که کاربر حذف کرده اون بلاگ رو) اونموقع اس که این ارور میاد و میگه که همچین چیزی پیدا نکردم یا همون page not found معروف
ارور 429: این برای وضعیتیه که کاربر به صورت رگباری و پشت هم ریکوئست میفرسته و خاااره سرور تار و مار میشه و همچین اروری برمیگردونه که آقا جون جدت یواش
همونطور ک توی مثال بالا میبینید ما نمونه api خودمون رو ایمپورت کردیم و از چیزی به اسم interceptors استفاده کردیم و interceptor ها دوتا هستن یکی برای request و یکی برای response اولی میاد یه سری بلا سر ریکوئست اولیه ما میاره و بعد میفرستتش سمت سرور، ولی دومی (response) که ما توی مثال استفاده کردیم، بعد از اینکه ما ریکوئست رو زدیم و جوابی از سرور برگشت، قبل از اینکه اون جواب به then و catch برسه به ایشون میرسه و حالا ما با متد use که دوتا ارگومان داره میتونیم یه بلایی سر این ریسپانسی که از سرور گرفتیم بیاریم و بعد بفرستیمش سمت then و catch
متد use دوتا کال بک میگیره که...
اولی رو فقط وقتی اجرا میکنه که ریسپانسی که از سرور گرفتیم وضعیتش زیر یا مساویه 200 باشه (موفقیت آمیز) و دیگه دومین کال بک رو در این صورت اجرا نمیکنه.
دومی رو فقط وقتی اجرا میکنه که ریسپانسی که از سرور گرفتیم وضعیتش بیشتر از 200 باشه ( شکست خورده) و دیگه اولین کال بک رو در این صورت اجرا نمیکنه.
توی مثال بالا برای فانکشن اول که کار خاصی با ریسپانس نداریم و در صورت موفقیت امیز بودن، ریسپانس رو همونطوری میفرستیم میره واسه .then
اماااا وقتی ریسپانس شکست خورده باشه معمولا بیشتر اوقات وضعیتش یکی از ایناس که اکثرشون رو میشناسید 401 , 400 , 403 , 429 و حالا ما اینجاس که توی کال بک دوم برنامه ها داریم واسه این ارور ها.
کال بک دوم یه error به صورت ارگومان گرفته که یه پراپرتی response داره که ابجکته و شامل یه سری پراپرتی هاس که ما اینجا فقط دوتاشو کار داریم : یکی وضعیت یا status و دیگری پیغامی که از بک اند برای اون ارور داده یا همون data.message حالا اومدیم گفتیم که هروقت ارور 400 گرفت بیا با promise.reject یه اعلام شکست بکن و در ادامش گفتیم یه الرت هم به کاربر بده و پیغامی که به کاربر میدیم میتونه قشنگ همون پیغامی باشه که ازبک اند میاد error.response.data.message
نکته : promise.reject رو توی همه ی حالات برمیگردونیم که وقتی ریسپانس از این مرحله میگذره و میره به then و catch ، سیستم بدونه که کدوم باید اجرا شه و کدوم نه
توی شرط بعدی گفتیم که اگه وضعیت 403 بود ریجکت کن و بعدش الرت بده که شما در سطح این قابلیت نیستی و راه باز جاده دراز!
توی شرط بعدی که ارور 404 هست میل سخن گفتن نیست و فقط ریجکت
توی شرط بعدی که ارور 429 هست ریجکت و بعد، آلرت دادیم که آراااام وحشی
توی شرط آخر که 401 هست به انقضای توکن برخوردیم و از اونجایی که که توکن و بقیه اطلاعاتی که موقع ثبت نام و لاگین میگیریم رو توی لوکال استوریج ذخیره کردیم، همشون رو پاک میکنیم و کاربر رو دوباره به صفحه لاگین میفرستیم تا دوباره لاگین کنه و با توکن جدید ادامه بده.
در نظر داشته باشید شما میتونید هر خلاقیتی به خرج بدید و واسه ارور ها ریسپانس های قشنگ تری بزک دوزک کنید و صرفا دلخوش به یه آلرت خشک و خالی نباشید.
حالا این کامپوننت AxiosErrorHandler رو هر جا صدا بزنید و اونجا ریکوئستی با api بزنید اون interceptor میپره وسط و کارشو میکنه و درصورت ارور اون اجی مجی های شرطی که نوشتیم رو اجرا میکنه
خب من میزارمش توی "src/index.js" که توی کل فیهاخالدونِ پروژه رهگیری انجام بشه:
3.پوشه Requests پوشه ایه که ما تموم ریکوست هامون رو با دسته بندی های مختلف به صورت فانشکن های async تعریف و اماده میکنیم تا برای استفاده و دسترسی به هر ریکوئستی فقط کافی باشه اون فانکشن موردنظر رو از این پوشه ایمپورت و صدا بزنیم و ریسپانسی که میگیریم رو استفاده کنیم.
ما معمولا ریکوئست هامون دسته بندی های مختلفی دارن مثلا یه سری ریکوئست داریم مربوط به authentication هست مثلا ریکوئست ثبت نام و یا لاگین و یا عوض کردن رمز عبور و یا فراموشی رمز عبور و... و یا مثلا یه سری ریکوئست داریم مربوط به خود کاربر هست مثلا گرفتن اطلاعات ثبت شده کاربر ، عکس کاربر ، پست های کاربر و.... بهتره این دسته بندی هارو توی پوشه Requests هرکدوم رو توی پوشه جدا بزاریم مثلا:
توی مثال بالا ما توی هر پوشه یه فایل index.js داریم و توی این فایلا تک به تک فانکشن هایی که لازم داریم رو میزاریم :
عکس بالا مثالی از فایل index پوشه auth هست که سه تا فانکشن مختلف برای ریکوئست های مختلف داریم که همه این ریکوئست ها مربوط به aithentication هست و اکسپورت شده اند تا ما بتونیم هر جا مثلا به ثبت نام نیاز داریم فقط این فانکشن رو ایمپورت و صداش کنیم و تمااام.
نکته: یادتون باشه این فانکشن ها async هستن و ما باید به صورت asyncصداشون کنیم و منظر جوابی که از سرور میگیرن و برمیگردونن باشیم ( با await یا با .then)
اینم یه مثال نهایی مثل بالا ممتها از پوشه blogs که فقط ریکوئست های مربوط به بلاگ هست:
بعضی وقتا میشه که ما توی useEffect ریکوئستمون رو صدا میزنیم و خب مسئله ای که هست و بعضی وقتها دیدم رعایت نمیشه اینه که ما وقتی توی این هوک یه ریکوئست میزنیم هر سری داره هی این ریکوئست رو میزنه و حتی وقتی که دیگه کامپوننت رو بستیم (به اصطلاح unmount کردیم) باز هم اون ریکوئسته داره زده میشه و اینجاس که شاید همچین هشداری رو دیده باشید:
این هشدار میده که عزیز من کامپوننتت بسته شده و بهش احتیاجی نداری بیا ولی با این حال داری ریکوئست میزنی!
کاری که ما باید انجام بدیم اینه که وقتی داخل useEffect یه ریکوئستی صدا میزنیم آخرش توی همین هوک یه فانکشن برگردونیم که توش اون ریکوئست رو کنسل کنیم ( این فانکشنی که برمیگردونیم وقتی اجرا میشه که کامپوننت unmount میشه) حالا روشی که میتونیم با axios اینکارو راحت انجام بدیم به این شکله:
ما به عنوان پارامتر دوم api، بهش یه ابجکت میدیم که پراپرتی signal برابر باشه با کنترلری که ساختیم و توی فانکشن cleanup، متد abortرو از کنترلری که ساختیم صدا میکنیم و این کار ریکوئست مارو کنسل میکنه و دیگه اون وارنینگ هم نمیگیریم و پرفورمنس هم به مراتب بهتر میشه.
خب این مقاله اینجا به پایان میرسه و امیدوارم کار با این کتابخونه دوست داشتنی و خفن رو یاد گرفته باشی و بیشتر و بهتر با ریکوئست ها دلبری کنی :)
خدانگهدار و موفق باشی?