مقدمه

در تاریخچه React، دو روش اصلی برای تعریف کامپوننت‌ها وجود داشته است: «کامپوننت‌های کلاس‌محور» (Class Components) و «کامپوننت‌های تابعی» (Functional Components). در نسخه‌های اولیه React، کامپوننت‌های کلاس‌محور تنها راه برای داشتن state و دسترسی به متدهای چرخه حیات (lifecycle methods) بودند. اما با معرفی «هوک‌ها» (Hooks) در React 16.8 کامپوننت‌های تابعی نیز به این قابلیت‌ها مجهز شدند و به روش استاندارد و ترجیحی برای ساخت کامپوننت‌ها تبدیل شدند.

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

نگاهی به کامپوننت‌های کلاس‌محور

یک کامپوننت کلاس‌محور، یک کلاس جاوااسکریپت ES6 است که از React.Component ارث‌بری می‌کند.

ساختار اصلی

این نوع کامپوننت حداقل به یک متد به نام render() نیاز دارد که مسئولیت برگرداندن JSX را بر عهده دارد. داده‌های ورودی (props) از طریق this.props و وضعیت داخلی (state) از طریق this.state قابل دسترسی هستند.

Copy Icon JAVASCRIPT (Class Component)
import React, { Component } from 'react';

class Welcome extends Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

export default Welcome;

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

مقایسه مستقیم: یک شمارنده ساده

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

پیاده‌سازی با کامپوننت کلاس‌محور

در این روش، ما state را در سازنده (constructor) مقداردهی اولیه می‌کنیم و برای به‌روزرسانی آن از متد this.setState() استفاده می‌کنیم.

Copy Icon JAVASCRIPT (Class Component)
class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

پیاده‌سازی با کامپوننت تابعی و هوک‌ها

حالا همان منطق را با استفاده از یک کامپوننت تابعی و هوک useState پیاده‌سازی می‌کنیم.

Copy Icon JAVASCRIPT (Functional Component)
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

با مقایسه این دو قطعه کد، مزایای کامپوننت تابعی به وضوح مشخص می‌شود:

  • کد کمتر و خواناتر: نیاز به تعریف class، constructor، super(props) و استفاده از this نیست.
  • منطق state سرراست‌تر: هوک useState به صورت مستقیم مقدار state و تابع به‌روزرسانی آن را در اختیار ما قرار می‌دهد که بسیار گویاتر از this.setState است.

چرا جامعه React به سمت هوک‌ها حرکت کرد؟

علاوه بر خوانایی، چند دلیل فنی عمیق‌تر نیز برای این مهاجرت وجود داشت. مدیریت منطق state پیچیده در متدهای چرخه حیات کلاس‌ها (مانند componentDidMount و componentDidUpdate) می‌توانست بسیار دشوار شود. هوک‌ها، به ویژه useEffect، راهی برای گروه‌بندی منطق‌های مرتبط با هم، صرف نظر از اینکه در کدام مرحله از چرخه حیات اجرا می‌شوند، فراهم کردند. این کار اشتراک‌گذاری منطق بین کامپوننت‌ها را نیز بسیار ساده‌تر کرد.

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