الگوهای رایج طراحی کامپوننت

در طول این فصل با اصول مختلفی مانند تفکر کامپوننتی، ترکیب، طراحی Props و کاهش Coupling آشنا شدیم. در این درس، چند الگوی رایج طراحی کامپوننت را مرور می‌کنیم که در پروژه‌های واقعی React زیاد دیده می‌شوند و در واقع ترکیبی از همان اصولی هستند که تا اینجا یاد گرفتیم.

الگوی Container و Presentational

در این الگو، کامپوننت‌ها به دو دسته تقسیم می‌شوند: کامپوننت‌های Container که مسئول نگهداری State یا دریافت داده هستند، و کامپوننت‌های Presentational که فقط بر اساس Props دریافتی، ظاهر را نمایش می‌دهند و هیچ منطق داده‌ای ندارند.

// Presentational: فقط نمایش
function UserList({ users }) {
  return (
    <ul>
      {users.map((u) => (
        <li key={u.id}>{u.name}</li>
      ))}
    </ul>
  );
}

// Container: مدیریت داده
function UserListContainer() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    fetchUsers().then(setUsers);
  }, []);

  return <UserList users={users} />;
}

این تفکیک باعث می‌شود کامپوننت UserList کاملاً مستقل از نحوه دریافت داده باشد و بتوان آن را در جاهای مختلف، حتی با منابع داده متفاوت، استفاده کرد.

الگوی Compound Components

در این الگو، چند کامپوننت مرتبط با هم به شکلی طراحی می‌شوند که در کنار هم و به‌صورت یک مجموعه معنادار استفاده شوند؛ مشابه رابطه بین تگ‌های select و option در HTML. برای مثال:

<Tabs>
  <Tabs.Tab label="پروفایل" />
  <Tabs.Tab label="تنظیمات" />
</Tabs>

در این الگو، کامپوننت والد (Tabs) معمولاً وضعیت مشترک را مدیریت می‌کند و کامپوننت‌های فرزند (Tabs.Tab) بدون نیاز به انتقال دستی Props زیاد، به آن وضعیت دسترسی دارند. جزئیات نحوه پیاده‌سازی این الگو معمولاً به Context نیاز دارد که در فصل مربوط به Context بررسی می‌شود.

الگوی Render Props

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

function MouseTracker({ render }) {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  return <div onMouseMove={(e) => setPosition({ x: e.clientX, y: e.clientY })}>
    {render(position)}
  </div>;
}

// استفاده
<MouseTracker render={(pos) => <p>{pos.x}, {pos.y}</p>} />

این الگو در گذشته بسیار رایج بود، اما با معرفی Custom Hooks (که در فصل ۱۰ بررسی می‌کنیم)، بسیاری از موارد استفاده آن با Custom Hookها جایگزین شده‌اند، چون Custom Hookها معمولاً خواناتر و ساده‌تر هستند.

این الگوها را حفظ نکنید

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

جمع‌بندی

الگوهایی مانند Container/Presentational، Compound Components و Render Props، راه‌حل‌های شناخته‌شده‌ای برای مسائل رایج طراحی کامپوننت هستند. هرکدام از این الگوها در موقعیت مشخصی مفید است و شناخت آن‌ها به ما کمک می‌کند تصمیم بهتری در طراحی کامپوننت‌ها بگیریم. با این درس، بخش مفهومی فصل دوم به پایان می‌رسد و در ادامه به سراغ آزمون فصل ۲ می‌رویم.