Functional Component — روشی برای ایجاد کامپوننتهای React با استفاده از توابع معمولی JavaScript است که پس از انتشار React 16.8 و ظهور هوکها به استاندارد تبدیل شد. کامپوننتهای تابعی props دریافت میکنند و عناصر React را برمیگردانند، بدون نیاز به ارثبری از React.Component. طبق دادههای React, 2024، 97% کامپوننتهای جدید در اکوسیستم React به صورت توابع نوشته میشوند.
نکات اصلی
Functional Component (کامپوننت تابعی) — کامپوننت React است که به صورت تابع تعریف میشود، شی props را دریافت میکند و عنصر React برمیگرداند. قبل از React 16.8 چنین کامپوننتهایی stateless (بدون حالت) نامیده میشدند، زیرا نمیتوانستند حالت داخلی را مدیریت کنند.
با ظهور هوکها در React 16.8 (فوریه 2019) کامپوننتهای تابعی به حالت (useState)، اثرات جانبی (useEffect)، زمینه (useContext) و مراجع (useRef) دسترسی یافتند. اصطلاح «stateless» منسوخ شده است — کامپوننتهای تابعی مدرن به طور کامل حالت را مدیریت میکنند.
React به طور رسمی کامپوننتهای تابعی را به عنوان روش اصلی ایجاد کامپوننتها توصیه میکند. تمام مستندات در react.dev، مثالها، آموزشها و APIهای جدید (Server Components, Actions) بر روی رویکرد تابعی ساخته شدهاند. کامپوننتهای کلاسی حذف نشدهاند، اما قابلیتهای جدید React فقط از طریق هوکها در دسترس هستند.
سادهترین کامپوننت تابعی — تابعی است که JSX برمیگرداند. آن props را به عنوان آرگومان دریافت میکند، نیاز به سازنده، فراخوانی super یا binding this ندارد. props در دسترس هستند مستقیماً از طریق آرگومان تابع — بدون this.props.
// Functional component without state
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
}
// Arrow function (common variant)
const Greeting = ({ name }) => (
<h1>Hello, {name}!</h1>
);
برای افزودن حالت از useState استفاده کنید. هوک یک چندتایی از دو مقدار برمیگرداند: حالت فعلی و تابع setter. بر خلاف this.setState کلاسی، useState مقدار را به طور کامل جایگزین میکند، نه اینکه با مقدار قبلی ادغام کند.
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
هوکها اساس React مدرن هستند. هر هوک یک وظیفه خاص را حل میکند: useState — حالت، useEffect — اثرات جانبی، useContext — دسترسی به زمینه، useRef — مراجع به عناصر DOM، useCallback و useMemo — memoization.
useEffect همزمان جایگزین componentDidMount، componentDidUpdate و componentWillUnmount میشود. آرگومان اول — callback با اثر، دوم — آرایه وابستگیها. اگر آرایه خالی باشد، اثر یک بار پس از mount اجرا میشود. اگر وابستگیها مشخص شدهاند — با هر تغییر در هر یک از آنها.
import React, { useState, useEffect } from 'react';
function UserData({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
let cancelled = false;
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
if (!cancelled) setUser(data);
});
// Cleanup - analog of componentWillUnmount
return () => { cancelled = true; };
}, [userId]); // Dependency
return <div>{user?.name}</div>;
}
مزیت اصلی کامپوننتهای تابعی — استفاده مجدد از منطق از طریق هوکهای سفارشی است. اگر در کامپوننت کلاسی منطق بین componentDidMount و componentDidUpdate تکرار میشد، در کامپوننت تابعی به یک تابع جداگانه با پیشوند use منتقل میشود. هوکهای سفارشی — جایگزین اصلی HOC و render props هستند.
React برای استفاده مجدد از کد بین کامپوننتها ترکیب را توصیه میکند، نه وراثت را. کامپوننتهای تابعی برای ترکیب ایدهآل هستند: یک کامپوننت میتواند شامل چندین کامپوننت دیگر باشد، props و children را به آنها منتقل کند.
الگوی children — وقتی کامپوننت عناصر فرزند را از طریق prop children دریافت میکند — به طور طبیعی پیادهسازی میشود: function Layout({ children }). مشابه slots در Vue از طریق props-کامپوننتهای نامگذاری شده پیادهسازی میشود: function Page({ header, sidebar, content }).
وراثت در React عملاً استفاده نمیشود. حتی کامپوننتهای کلاسی که از نظر فنی extends را پشتیبانی میکنند، به ندرت سلسلهمراتب عمیقتر از یک سطح تشکیل میدهند. کامپوننتهای تابعی با ترکیب نیازهای استفاده مجدد از کد را بدون وراثت کاملاً پوشش میدهند.
کامپوننتهای تابعی مکانیسمهای بهینهسازی داخلی دارند. React.memo — مشابه PureComponent برای توابع: از رندر مجدد جلوگیری میکند اگر props تغییر نکرده باشند (مقایسه سطحی). useMemo نتیجه محاسبات را بین رندرها کش میکند.
import React, { useMemo } from 'react';
// React.memo - memoize component
const ExpensiveList = React.memo(({ items }) => {
return <ul>
{items.map(item =>
<li key={item.id}>{item.name}</li>
)}
</ul>;
});
// useMemo - caching result
function Dashboard({ transactions }) {
const summary = useMemo(() => {
return transactions.reduce((acc, t) => acc + t.amount, 0);
}, [transactions]);
return <p>Total: {summary}</p>;
}
useCallback توابع callback را memoize میکند تا در هر رندر دوباره ایجاد نشوند. این هنگام ارسال توابع به کامپوننتهای فرزند که در React.memo پیچیده شدهاند حیاتی است: بدون useCallback کامپوننت فرزند هر بار رندر میشود، زیرا reference به تابع تغییر میکند.
تفاوت بین رویکردها فراتر از نحو است. کامپوننتهای تابعی — یک closure هستند: حالت و اثرات به یک رندر خاص متصل هستند. کامپوننتهای کلاسی — this هستند: حالت همیشه از this.state گرفته میشود که ممکن است در زمان اجرای callback ناهمگام منسوخ شده باشد.
| جنبه | تابعی | کلاسی |
|---|---|---|
| حالت | useState (منزوی) | this.state + this.setState |
| اثرات | useEffect با وابستگیها | componentDidMount + componentDidUpdate |
| زمینه | useContext | Context.Consumer یا static contextType |
| بهینهسازی | React.memo + useMemo | PureComponent + shouldComponentUpdate |
| استفاده مجدد از منطق | هوکهای سفارشی | HOC / Render props |
| this | ندارد | bind در سازنده |
کامپوننتهای تابعی جریان اجرای خطی را فراهم میکنند. در کامپوننت کلاسی منطق مرتبط اغلب در چندین متد چرخه حیات پخش میشود. به عنوان مثال، اشتراک WebSocket در componentDidMount تنظیم میشود و در componentWillUnmount حذف میشود. در کامپوننت تابعی هر دو عملیات — در یک useEffect با تابع بازگشتی پاکسازی.
TypeScript با کامپوننتهای تابعی طبیعیتر از کلاسها کار میکند. نوعدهی props — یک اینترفیس برای آرگومان تابع. نوع مقدار بازگشتی — JSX.Element یا ReactNode. useState به طور خودکار نوع را از مقدار اولیه استنتاج میکند.
import React, { useState } from 'react';
interface ButtonProps {
label: string;
variant?: 'primary' | 'secondary';
onClick: () => void;
}
const Button: React.FC<ButtonProps> =
({ label, variant = 'primary', onClick }) => (
<button
className={`btn btn-${variant}`}
onClick={onClick}
>
{label}
</button>
);
TypeScript مدرن امکان نوعدهی useReducer با انواع union تفکیکشده، Generics در هوکهای سفارشی و بررسی دقیق children از طریق React.PropsWithChildren را فراهم میکند. کامپوننتهای تابعی با TypeScript امنیت کامل نوع را بدون انتزاعات اضافی ارائه میدهند.
سوالات متداول
کامپوننتهای تابعی سادهتر هستند: سازنده، bind، this و متدهای پراکنده چرخه حیات وجود ندارد. هوکها امکان گروهبندی منطق مرتبط را فراهم میکنند. React منابع خود را روی توسعه هوکها سرمایهگذاری میکند، نه کلاسها. Server Components و ویژگیهای جدید فقط در توابع در دسترس هستند.
بله، از طریق useEffect با تایمر در داخل و تابع پاکسازی برای clearInterval. به closure توجه کنید: اگر شمارنده را تیک میزنید، از setState به صورت تابعی استفاده کنید: setCount(c => c + 1)، تا به مقدار قدیمی در closure وابسته نباشید.
از useRef برای ذخیره مقدار قبلی استفاده کنید. ref را در useEffect پس از هر رندر بهروزرسانی کنید. برای مقایسه بصری هوک سفارشی usePrevious کمک میکند: const prev = usePrevious(value);
React.memo — HOC برای memoization کل کامپوننت: از رندر مجدد جلوگیری میکند اگر props تغییر نکرده باشند. useMemo — هوک برای memoization یک مقدار خاص در داخل کامپوننت: نتیجه تابع را بین رندرها بر اساس آرایه وابستگیها کش میکند.
سادهترین راه — useState با مقداردهی اولیه از props + useEffect با وابستگی به props برای همگامسازی. برای موارد نادر React توصیه میکند: از کلید (key) برای بازآفرینی اجباری کامپوننت هنگام تغییر دادههای ورودی استفاده کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید