برنامه نویسی

useCallback – رمزگشایی از React Hooks (Pt. 1)

در این مقاله، زمان و نحوه استفاده از React را بررسی خواهیم کرد useCallback قلاب و اشتباهی که اکثر توسعه دهندگان جوان مرتکب شده اند.

اگر می‌خواهید این را ببندید و به صورت محلی اجرا کنید، مخزن را می‌توانید در اینجا پیدا کنید

شروع شدن

  • fork and clone
  • cd client
  • npm i
  • npm start

برابری مرجع

برابری ارجاعی یک مفهوم اساسی در جاوا اسکریپت و علوم کامپیوتر به عنوان یک کل است. پس بیایید با نمایش آن در عمل شروع کنیم.

شما به سادگی می توانید همراه یا اجرا کنید referentialEquality.js برای مشاهده خروجی

console.log(1 === 1);
// prints true
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هنگام ارزیابی اینکه آیا integer 1 کاملاً برابر است integer 1، کنسول چاپ می کند true. این به این دلیل است که، خوب … integer 1 است کاملاً برابر با integer 1.

هنگام ارزیابی دو رشته، نتیجه یکسانی را مشاهده می کنیم.

console.log('Referential Equality' === 'Referential Equality');
// prints true
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بدیهی است که این همیشه برای دو نوع داده اولیه با یک مقدار صادق خواهد بود.

حال، ساختار داده چطور؟ برای مثال، دو شئ با جفت کلید/مقدار یکسان؟ در مورد لفظ شیء خالی چطور؟

console.log({ a: 1 } === { a: 1 });
// prints false
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چرا این چاپ false? هنگام مقایسه اینکه آیا این دو لفظ شی کاملاً برابر هستند یا خیر، جاوا اسکریپت از متن مربوط به آنها استفاده می کند آدرس های حافظه.

به عبارت دیگر، این دو شیء ممکن است حاوی یکسان باشند ارزش های، اما آنها نیستند ارجاع به همان شی. آنها نگاه کن یکسان است اما دو متفاوت را اشغال می کند فضاهای حافظه.

این امر چه در حال مقایسه دو کلمه شیء، دو کلمه آرایه یا دو تابع باشد، صدق می کند!

console.log({} === {});
// prints false

console.log([1, 2, 3] === [1, 2, 3]);
// prints false

console.log([] === []);
// prints false
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای نشان دادن بیشتر این موضوع، یک تابع تعریف می کنیم func، که یک تابع ناشناس را برمی گرداند که به نوبه خود چیز دیگری را برمی گرداند (مانند یک عنصر JSX).

const func = () => {
  return () => 'This is a pretend component.';
};
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس دو تابع مختلف را اختصاص می دهیم، firstRender و secondRender، برابر با مقدار بازگردانده شده توسط func.

const firstRender = func();

const secondRender = func();
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در فکر func به عنوان جزء عملکردی React شما، در حالی که firstRender یک تابع _inside_ از آن در اولین رندر است و secondRender یک تابع _inside_ آن در رندر دوم است.

بااینکه firstRender و secondRender یکسان به نظر می رسند، همان مقدار را برمی گردانند، و حتی مقدار آنها از همان تعریف به آنها اختصاص داده می شود، آنها ندارند برابری ارجاعی. در نتیجه هر بار که مولفه والد رندر می شود، این تابع را دوباره تعریف می کند.

console.log(firstRender === secondRender);
// false
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

متأسفانه، در جاوا اسکریپت، چاپ این آدرس های حافظه مانند پایتون آسان نیست، اما برای توضیح کمی عمیق تر از مرجع در مقابل مقدار، به این مقاله نگاهی بیندازید.

این موضوع می تواند متراکم شود و نیازی نیست امشب کلاسی در مورد آن تدریس کنید. بنابراین در حال حاضر، فقط به یاد داشته باشید:

  • نوع داده اولیه === نوع داده اولیه
  • ساختار داده ها !== ساختار داده ها.

با وجود برابری ارجاعی، بیایید به کد React خود بپردازیم و ببینیم چرا این موضوع مرتبط است.

کد شروع

با مشاهده کد ارائه شده شروع کنید، سپس ابزار توسعه دهنده خود را باز کنید. تا مدتی دیگر از کنسول مرورگر استفاده خواهیم کرد.

پس از چرخش برنامه خود، آن را باز کنید BookDetails.jsx جزء و دوباره ذخیره کنید. اولین چیزی که ممکن است در سرور توسعه React خود متوجه شویم، یک چیز رایج است WARNING که توسعه دهندگان جوان تمایل به نادیده گرفتن آن دارند. همانطور که شما به نیروی کار ضربه می زنید و شروع به نوشتن کد برای تولید می کنید، لنگرهای شما حتی سخت تر از آنچه در آن تعبیه شده است می شود. create-react-app. WARNINGS تبدیل خواهد شد ERRORS، و برخی از قوانین لینتر حتی به شما اجازه نمی دهند بدون پرداختن به این موارد فشار دهید ERRORS.

و خود را آماده کن؛ اکثر لنگرها اجازه نمی دهند console.logs در کد شما بنابراین هر چه زودتر روش صحیح را یاد بگیرید، بهتر است. بنابراین به جای نادیده گرفتن آن، بیایید نحوه درمان آن را دریابیم.

WARNING in [eslint]
src/components/BookDetails.jsx
  Line 18:6:  React Hook useEffect has a missing dependency: 'getBookDetails'. Either include it or remove the dependency array  react-hooks/exhaustive-deps

webpack compiled with 1 warning
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

توجه: ممکن است ابتدا لازم باشد BookDetails.jsx را دوباره برای ایجاد این ذخیره کنید WARNING

اگر در React Docs بگردیم، می‌توانیم راه‌حل‌های پیشنهادی نیمه گیج‌کننده برای این موضوع را رمزگشایی کنیم. WARNING به شرح زیر است:


لحظه ای به عواقب هر گزینه فکر کنید.

  1. شامل تعریف تابع در داخلuseEffect

ما نمی توانیم این تابع را در جای دیگری فراخوانی کنیم مگر اینکه آن را دوباره تعریف کنیم.

  1. آرایه وابستگی را حذف کنید.

این باعث می شود useEffect هر زمان وضعیت یا props تغییر می‌کند، معمولاً باعث ایجاد یک رندر مجدد بی‌نهایت می‌شود، و در مورد ما، می‌تواند API ما را با درخواست‌های بی‌نهایت بارگذاری کند.

  1. فراخوانی تابع را از قسمت حذف کنید useEffect.

تابع فراخوانی نمی شود.

  1. تابع را در آرایه وابستگی قرار دهید.

اولین باری که کامپوننت رندر می‌شود، تابع ما را تعریف می‌کند، که باعث می‌شود useEffect، که باعث می‌شود کامپوننت دوباره رندر شود، که تابع را دوباره تعریف می‌کند، که باعث می‌شود useEffect، که باعث می‌شود کامپوننت دوباره رندر شود. ، که عملکرد را دوباره تعریف می کند …

بنابراین … یک توسعه دهنده چه کاری باید انجام دهد؟


ساده‌ترین و ارجح‌ترین راه‌حل، «شامل آن» است، یعنی جابه‌جایی getBookDetails تعریف تابع در داخل useEffect. این به یک اصل برنامه نویسی شی گرا معروف به کپسولاسیون پایبند است.

اما فرض کنید می دانیم که باید تابع را در جای دیگری فراخوانی کنیم. آیا باید بعداً آن را دوباره تعریف کنیم؟ این برای ما خیلی خشک نیست.

بیایید آرایه وابستگی خود را به گونه ای تغییر دهیم که مرجع تابع ما باشد. شما useEffect اکنون باید به این شکل باشد

useEffect(() => {
  getBookDetails();
}, [getBookDetails]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و getBookDetails تعریف شده باقی می ماند در بالا را useEffect.

const getBookDetails = async () => {
  const res = await axios.get(`${BASE_URL}/${id}`);
  setBook(res.data);
  console.log(res.data);
};
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حالا ما یک جدید داریم WARNING:

WARNING in [eslint]
src/components/BookDetails.jsx
  Line 10:9:  The 'getBookDetails' function makes the dependencies of useEffect Hook (at line 18) change on every render. Move it inside the useEffect callback. Alternatively, wrap the definition of 'getBookDetails' in its own useCallback() Hook  react-hooks/exhaustive-deps

webpack compiled with 1 warning
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

را وارد کنید useCallback قلاب

به طور خلاصه، useCallback hook به شما امکان می‌دهد تا یک تابع را بین رندرهای مجدد مؤلفه‌تان در حافظه پنهان ذخیره کنید. کار مشابهی را انجام می دهد useMemo، تفاوت های ظریف که در مقاله دیگری به آن خواهیم پرداخت.

اگر این موضوع به شما علاقه مند است، می توانید اطلاعات بیشتری در مورد آن در React Docs بخوانید.

لطفا به هشدار آنها توجه کنید:

  • فقط باید به آن تکیه کنید useCallback به عنوان یک بهینه سازی عملکرد اگر کد شما بدون آن کار نمی کند، مشکل اساسی را پیدا کنید و ابتدا آن را برطرف کنید. سپس می توانید اضافه کنید useCallback برای بهبود عملکرد

useCallback نحو

useCallback نحو بسیار شبیه به useEffect نحو، که ما قبلا می دانیم. به اسکلت هر کدام نگاه کنید.

useEffect(() => {}, []);

useCallback(() => {}, []);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تفاوت جزئی با useEffect، به تابع ناشناس می گوییم که تابع ما را در حین با اجرا کند useCallback، مقدار بازگشتی را به مرجعی اختصاص می دهیم تا در جای دیگری فراخوانی شود.

ابتدا وارد می کنیم useCallback از جانب 'react'. به جای افزودن یک خط جدید، بهتر است آن را همراه با سایر واردات خود تخریب کنیم.

import { useState, useEffect, useCallback } from 'react';
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اکنون می توانیم اختصاص دهیم getBookDetails به مقدار بازگشتی از a useCallback فراخوانی تابع

const getBookDetails = useCallback();
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس تمام نحو برای را اضافه می کنیم useCallback. آرایه وابستگی خود را به خاطر بسپارید!

const getBookDetails = useCallback(() => {}, []);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در مثال ما نیاز داریم async قبل از پارامترهای ما

const getBookDetails = useCallback(async () => {}, []);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و در نهایت منطق تابع خود را به بلوک کد اضافه می کنیم.

const getBookDetails = useCallback(async () => {
  const res = await axios.get(`${BASE_URL}/${id}`);
  setBook(res.data);
  console.log(res.data);
}, []);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هنگامی که ما ذخیره می کنیم، می گیریم … دیگری WARNING.

WARNING in [eslint]
src/components/BookDetails.jsx
  Line 14:6:  React Hook useCallback has a missing dependency: 'id'. Either include it or remove the dependency array  react-hooks/exhaustive-deps

webpack compiled with 1 warning
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بیایید یک لحظه به این موضوع فکر کنیم.

چرا باید آرایه وابستگی ما ردیابی شود id متغیر؟

اگر ارزش از id تغییرات، «getBookDetails» باید به نقطه پایانی دیگری برسد، بنابراین React باید آن را دوباره تعریف کند.

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

const getBookDetails = useCallback(async () => {
  const res = await axios.get(`${BASE_URL}/${id}`);
  setBook(res.data);
  console.log(res.data);
}, [id]);

useEffect(() => {
  getBookDetails();
}, [getBookDetails]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و بالاخره همین! ما در سرور React dev سبز رنگ می بینیم. لینتر شاد یک توسعه دهنده ارشد خوشحال است. و یک توسعه دهنده ارشد خوشحال شما را خوشحال می کند!

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

منابع

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا