کامپوننتها و prop ها
کامپوننتها به شما اجازه میدهند رابط کاربری را به بخشهای مستقل قابل استفاده مجدد تقسیم کنید و این بخشها را به صورت مجزا در نظر بگیرید. این صفحه مقدمهای برای مفهوم کامپوننت به شما ارائه میدهد. شما میتوانید مرجع API کامپوننت با جزئیات بیشتر را اینجا پیدا کنید.
از لحاظ مفهومی کامپوننتها همانند توابع جاوااسکریپت هستند. آنها ورودیهای دلخواه خود (props) را دریافت میکنند و المنتهایی از ریاکت را باز میگردانند که توصیف کننده آنچه باید روی صفحه نمایان شود است.
کامپوننتهایی از جنس تابع و کلاس
سادهترین راه برای تعریف کردن یک کامپوننت، نوشتن یک تابع جاوااسکریپت است:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
این تابع یک کامپوننت ریاکت قابلقبول است، زیرا یک “props” (که کوچک شده properties است) که آرگومانی از جنس شی همراه با داده است را دریافت و یک المنت ریاکت را باز میگرداند. به این نوع کامپوننتها، «کامپوننتهای تابع» گفته میشود زیرا آنها در واقعیت تابعهایی از جاوااسکریپت هستند.
شما همچنین میتوانید از کلاسهای ES6 برای تعریف کامپوننتها استفاده کنید:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
دو کامپوننت بالا از نظر ریاکت یکسان هستند.
کامپوننتهای برپایه کلاس و تابع هردو ویژگیهای منحصر بفرد دیگری نیز دارند که در بخشهای بعدی به آنها خواهیم پرداخت. تا آن زمان به دلیل اختصار از کامپوننتهای تابع استفاده میکنیم.
رندر کردن یک کامپوننت
پیشتر ما فقط با المنتهایی از ریاکت مواجه شدیم که بیانگر تگهای DOM بودند:
const element = <div />;
اما المنتها میتوانند بیانگر کامپوننتهایی که توسط کاربر تعریف شده نیز باشند:
const element = <Welcome name="Sara" />;
زمانی که ریاکت با المنتی مواجه میشود که بیانگر یک کامپوننت تعریف شده توسط کاربر باشد، ویژگیها و فرزندان JSX را در قالب یک شی واحد به کامپوننت منتقل میکند. ما این شی را “props” مینامیم.
برای مثال کد زیر “Hello, Sara” را روی صفحه رندر میکند:
function Welcome(props) { return <h1>Hello, {props.name}</h1>;
}
const root = ReactDOM.createRoot(document.getElementById('root'));
const element = <Welcome name="Sara" />;root.render(element);
بیایید جمعبندی کنیم که در این مثال چه اتفاقی میافتد:
۱. ما تابع root.render()
را با المنت <Welcome name="Sara" />
فرا میخوانیم.
۲. ریاکت کامپوننت Welcome
را به همراه {name: 'Sara'}
که در ورودی تابع به عنوان props
تعریف شده است، فرا میخواند.
۳. کامپوننت Welcome
ما یک المنت <h1>Hello, Sara</h1>
را به عنوان نتیجه باز میگرداند.
۴. DOM ریاکت به صورت بهینه DOM را بهروزرسانی میکند تا <h1>Hello, Sara</h1>
را نمایش دهد.
نکته: همیشه نام کامپوننتها را با یک حرف بزرگ شروع کنید.
ریاکت با کامپوننتهایی که با حروف کوچک شروع شده باشند همانند تگهای DOM برخورد میکند. برای مثال تگ
<div />
بیانگر یک تگ HTML است , اما<Welcome />
بیانگر یک کامپوننت است و بنابرین لازم استWelcome
در scope باشد.برای یادگیری دلیل این قرارداد، لطفا JSX In Depth را مطالعه فرمایید.
ترکیب کامپوننتها
کامپوننتها میتوانند در خروجی خود به دیگر کامپوننتها اشاره کنند. این به ما اجازه میدهد تا یک abstraction کامپوننت را برای هر سطحی از جزییات استفاده کنیم. در برنامههای ریاکتی یک دکمه، یک فرم، یک دیالوگ، و یک صفحه همگی به صورت کامپوننت بیان میشوند.
برای مثال ما میتوانیم یک کامپوننت به نام App
بسازیم که Welcome
را بارها رندر میکند:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Sara" /> <Welcome name="Cahal" /> <Welcome name="Edite" /> </div>
);
}
معمولا، پروژههای جدید ریاکت دارای یک کامپوننت App
واحد در بالاترین سطح هستند. اما اگر شما ریاکت را با پروژهای موجود ادغام میکنید، میتوانید از سطوح پایین به بالا طراحی کنید، برای مثال از یک کامپوننت کوچک مانند Button
شروع کنید و تدریجا تا بالای سلسله مراتب view طراحی را ادامه دهید.
استخراج کامپوننتها
از این که کامپوننتها را به کامپوننتهای کوچکتر تبدیل کنید هراسان نباشید.
برای مثال این کامپوننت Comment
را در نظر بگیرید:
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<img className="Avatar"
src={props.author.avatarUrl}
alt={props.author.name}
/>
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
این کامپوننت author (یک شیء)، text (یک string)، و date (یک تاریخ) را به عنوان props دریافت و یک دیدگاه را در یک شبکه اجتماعی توصیف میکند.
به علت تودرتو بودن این کامپوننت ، تغییر آن میتواند دشوار باشد، و همچنین استفاده مجدد از اجزای مستقل آن آسان نیست. بیایید چند کامپوننت را از آن استخراج کنیم تا این دشواریها را رفع کنیم.
در ابتدا Avatar
را استخراج مینماییم:
function Avatar(props) {
return (
<img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} /> );
}
کامپوننت Avatar
نیازی به دانستن اینکه قرار است در داخل یک Comment
رندر شود ندارد. برای همین به prop آن بجای author
اسمی عام نظیر user
دادهایم.
ما پیشنهاد میکنیم که propsها از دیدگاه خود کامپوننت نامگذاری شوند، نه بستری که قرار است در آن بهکار گرفته شوند.
حال میتوانیم Comment
را کمی ساده کنیم:
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<Avatar user={props.author} /> <div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
سپس ما کامپوننت UserInfo
را استخراج میکنیم که Avatar
را در کنار اسم کاربر رندر میکند:
function UserInfo(props) {
return (
<div className="UserInfo"> <Avatar user={props.user} /> <div className="UserInfo-name"> {props.user.name} </div> </div> );
}
این به ما کمک میکند تا Comment
را حتی بیشتر از این ساده کنیم:
function Comment(props) {
return (
<div className="Comment">
<UserInfo user={props.author} /> <div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
استخراج کامپوننتها شاید در ابتدا به نظر کار سختی بیاید، اما داشتن یک پالت از کامپوننتهایی با قابلیت استفاده مجدد در اپهای بزرگتر، هزینه آن را باز میگرداند. به عنوان یک قانون کلی در نظر داشته باشید که اگر قسمتی از رابط کاربری شما بارها استفاده شده است (نظیر Button
, Panel
, Avatar
) و یا اینکه قسمتی از رابط کاربری پیچیدگی منحصر به فرد خود را دارد (نظیر App
, FeedStory
, Comment
) آن قسمت کاندیدای مناسبی برای استخراج و تبدیل شدن به یک کامپوننت مجزا است.
propها فقط قابل خواندن هستند
بدون در نظر گرفتن اینکه شما یک کامپوننت را به عنوان یک تابع یا یک کلاس تعریف کردهاید، آن کامپوننت نباید هرگز propهای خودش را تغییر دهد.
این تابع sum
را در نظر بگیرید:
function sum(a, b) {
return a + b;
}
اینگونه توابع “خالص (pure)” نامیده میشوند زیرا سعی نمیکنند که ورودی خود را تغییر دهند و همیشه با ورودیهای مشخص خروجیهای ثابتی را نمایش میدهند.
در نقطه متقابل این کامپوننت “ناخالص (impure)” است زیرا ورودی خود را تغییر میدهد:
function withdraw(account, amount) {
account.total -= amount;
}
ریاکت خیلی منعطف است اما یک قانون سختگیرانه دارد:
تمامی کامپوننتهای ریاکت باید نسبت به propهای خود همانند توابع خالص عمل کنند.
البته رابطهای کاربری اپها داینامیک هستند و با زمان تغییر میکنند. در بخش بعدی، ما یک مفهوم جدید به نام “state” را معرفی میکنیم. State به کامپوننتهای ریاکت اجازه میدهد تا خروجی خود را در جواب به عملکرد کاربر، پاسخهای دریافتی از شبکه، و هر چیز دیگر در زمان مورد نیاز بدون اینکه این قانون را نقض کنند تغییر دهند.