طراحی API مناسب برای Custom Hook

یک Custom Hook در واقع رابطی است بین کامپوننت و منطقی که پشت آن پنهان شده. مثل هر رابط دیگری، طراحی آن اهمیت دارد: چه ورودی‌هایی بگیرد، چه چیزی برگرداند و چگونه نام‌گذاری شود. یک API خوب استفاده از Hook را ساده و اشتباه کردن را سخت می‌کند.

ورودی‌ها: فقط آنچه واقعاً متغیر است

پارامترهای Hook باید چیزهایی باشند که کامپوننت‌های مختلف واقعاً متفاوت به آن می‌دهند. مقدارهایی که همیشه ثابت‌اند را داخل خود Hook نگه دارید. برای پارامترهای اختیاری، مقدار پیش‌فرض تعریف کنید تا فراخوانی ساده بماند:

function useCounter(initialValue = 0, step = 1) {
  const [count, setCount] = useState(initialValue);

  function increment() {
    setCount((c) => c + step);
  }

  return { count, increment };
}

ورودی‌های زیاد: استفاده از Object گزینه‌ها

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

// مبهم: هر عدد چه معنایی دارد؟
useCounter(10, 2, 0, 100);

// روشن‌تر
useCounter({ initial: 10, step: 2, min: 0, max: 100 });

خروجی: آرایه یا Object؟

هر دو الگو رایج‌اند و انتخاب بین آن‌ها به نوع Hook بستگی دارد:

  • آرایه وقتی خروجی کوتاه (معمولاً دو مقدار) است و مصرف‌کننده احتمالاً نام‌های مختلفی برای آن‌ها انتخاب می‌کند؛ مثل useState که با [value, setValue] هر نامی می‌پذیرد.
  • Object وقتی تعداد مقادیر بیشتر است؛ چون مصرف‌کننده می‌تواند فقط موارد موردنیاز را بردارد و ترتیب مهم نیست.
// آرایه: دو مقدار با نام دلخواه
const [isOpen, toggle] = useToggle();

// Object: مقادیر متعدد، برداشتن انتخابی
const { data, loading, error } = useFetch(url);

نام‌گذاری گویا

نام Hook باید با use شروع شود و کاری که انجام می‌دهد را مشخص کند: useWindowWidth بهتر از useSize و بسیار بهتر از useData است. نام مقادیر برگشتی هم باید معنای خودشان را بیان کنند؛ isOpen گویاتر از flag است.

فقط آنچه لازم است را برگردانید

هر مقداری که از Hook برگردانید، بخشی از API عمومی آن می‌شود و بعداً تغییرش سخت است. اگر مقدار یا تابعی فقط برای پیاده‌سازی داخلی لازم بود، آن را برنگردانید. هرچه سطح رابط کوچک‌تر باشد، تغییر پیاده‌سازی داخلی آسان‌تر است.

ورودی از نوع تابع

گاهی مصرف‌کننده باید کدی را به Hook بدهد که در زمان مناسب اجرا شود، مثل onSuccess. Hook می‌تواند چنین تابعی را بگیرد و در لحظه لازم صدا بزند. در این حالت باید با دقت به Dependency Array و رندرهای مجدد فکر کرد؛ موضوعی که در فصل مربوط به Performance کامل‌تر بررسی می‌شود.

مثال قابل اجرا

در این مثال، Hook با یک Object گزینه‌ها پیکربندی می‌شود و یک Object برمی‌گرداند. مقدار min و max را عوض کنید و رفتار را ببینید.

useCounter با گزینه‌ها و خروجی Object
برای دیدن پیش‌نمایش کامپوننت، روی Run بزنید.

جمع‌بندی

API خوب برای Custom Hook یعنی ورودی‌های حداقلی با مقدار پیش‌فرض، Object گزینه‌ها برای پارامترهای زیاد، خروجی آرایه‌ای برای دو مقدار کوتاه و Object برای مقادیر بیشتر، نام‌های گویا و بازگرداندن فقط آنچه لازم است. در درس بعدی می‌بینیم چگونه Custom Hookها را با هم ترکیب کنیم.