ارسال داده ها به یک کامپوننت در React
کامپوننتهای React از پراپها برای ارتباط با همدیگر استفاده میکنند. هر کامپوننت والد میتواند با دادن پراپها، اطلاعاتی را به کامپوننتهای فرزند خود انتقال دهد. پراپها ممکن است شما را به یاد ویژگیهای HTML بیندازند، با این تفاوت که میتوانید هر مقدار جاوااسکریپتی ای از جمله اشیا، آرایهها و توابع را نیز از طریق آنها به کامپوننت ارسال کنید.
ورودهای آشنا
پراپها اطلاعاتی هستند که به تگهای JSX ارسال میکنید. به عنوان مثال، className، src، alt، width و height برخی از ورودیهایی هستند که میتوانید به تگ <img> ارسال کنید:
function Avatar() {
return (
<img
className="avatar"
src="https://i.imgur.com/1bX5QH6.jpg"
alt="Lin Lanying"
width={100}
height={100}
/>
);
}
export default function Profile() {
return (
<Avatar />
);
}
body { min-height: 120px; }
.avatar { margin: 20px; border-radius: 50%; }
این ورودی ها که به تگ <img> ارسال شده اند ورودی های پیش فرض این تگ هستند (ReactDOM با استاندارد HTML مطابقت دارد). اما برای سفارشی سازی کامپوننتهای خود مانند <Avatar> می توانید هر ورودی ای را از طریق پراپ ها ارسال کنید.
ارسال پراپها به یک کامپوننت
در این کد، کامپوننت Profile هیچ پراپی به کامپوننت فرزند خود، Avatar، ارسال نمیکند:
export default function Profile() {
return (
<Avatar />
);
}
شما میتوانید در دو مرحله به کامپوننت Avatar ورودی بدهید.
مرحله ۱: ارسال پراپها به کامپوننت فرزند
ابتدا، چند پراپ به Avatar ارسال کنید. به عنوان مثال، بیایید دو پراپ ارسال کنیم: person (یک شی) و size (یک عدد):
export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
size={100}
/>
);
}
اگر علامت دو آکولاد بعد از person= شما را گیج کند، به یاد داشته باشید که این همان شی است که در آکولادهای JSX قرار گرفته.
حالا میتوانید این پراپها را داخل کامپوننت Avatar بخوانید.
مرحله ۲: خواندن پراپها در کامپوننت فرزند
میتوانید این پراپها را بعنوان ورودی تابع Avatar و با قرار دادن نامهایشان person, size بین ({ و }) بعد از function Avatar بخوانید. این کار به شما اجازه میدهد از آنها در کامپوننت Avatar مثل یک متغیر استفاده کنید.
function Avatar({ person, size }) {
// person and size are available here
}
حال باید منطق کامپوونت Avatar را با استفاده از ورودی های person و size برای رندر کردن اطلاعات، اضافه کنید، و کار تمام است.
حالا میتوانید Avatar را با پراپهای مختلف به روشهای مختلف رندر کنید. سعی کنید مقادیر ورودی کامپوننت را تغییر دهید!
مشاهده نتیجه کد زیر در CodeSandBox
import { getImageUrl } from './utils.js';
function Avatar({ person, size }) {
return (
<img
className="avatar"
src={getImageUrl(person)}
alt={person.name}
width={size}
height={size}
/>
);
}
export default function Profile() {
return (
<div>
<Avatar
size={100}
person={{
name: 'Katsuko Saruhashi',
imageId: 'YfeOqp2'
}}
/>
<Avatar
size={80}
person={{
name: 'Aklilu Lemma',
imageId: 'OKS67lh'
}}
/>
<Avatar
size={50}
person={{
name: 'Lin Lanying',
imageId: '1bX5QH6'
}}
/>
</div>
);
}
export function getImageUrl(person, size = 's') {
return (
'https://i.imgur.com/' +
person.imageId +
size +
'.jpg'
);
}
پراپها کامپوننت را به یک جعبه سیاه تبدیل می کنند و به شما این امکان را میدهند که درباره کامپوننتهای والد و فرزند به طور مستقل فکر کنید. به عنوان مثال، ورودی های person یا size را در کامپوننت Profile بدون نیاز به دانستن نحوه استفاده Avatar از آنها می توانید تغییر دهید. به همین ترتیب، میتوانید نحوه استفاده Avatar از این پراپها را بدون اینکه به Profile نگاه کنید، تغییر دهید.
میتوانید پراپها را مانند "پیچهای تنظیم" تصور کنید که میتوانید آنها با را چرخاندن تنظیم کنید. آنها نقشی مشابه آرگومانها برای توابع ایفا میکنند—در واقع، پراپها تنها آرگومان برای کامپوننت شما هستند! توابع کامپوننت در React، شی ای بعنوان آرگومان (مانند props در مثال زیر) دریافت میکنند:
function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}
معمولاً به کل شی props نیازی نیست، بنابراین آن را به پراپهای تکی تجزیه میکنید.
جفت آکولادهای را هنگام اعلام پراپها درون ( و ) فراموش نکنید:
function Avatar({ person, size }) {
// ...
}
این دستور "تجزیه نامیده می شود و معادل خواندن پراپرتی شی است:
function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}
تعیین مقدار پیشفرض برای یک پراپ
برای تعریف مقدار پیشفرض برای یک ورودی، می توانید در زمان تجزیه مقابل نام ورودی علامت مساوی = را بهمراه مقدار پیشفرض قرار دهید تا درصورت ارسال نشدن پراپ، این مقدار برای ورودی ست شود:
function Avatar({ person, size = 100 }) {
// ...
}
حالا، اگر <Avatar person={...} /> بدون ورودی size استفاده شود، مقدار 100 برای size تعیین میشود.
مقدار پیشفرض تنها در صورتی استفاده میشود که پراپ size موجود نباشد یا به صورت size={undefined} ارسال شود. اما اگر به صورت size={null} یا size={0} ارسال شود، مقدار پیشفرض استفاده نخواهد شد.
ارسال پراپها با نحوه گسترش JSX
گاهی اوقات، ارسال پراپها بسیار تکراری میشود:
function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}
هیچ اشکالی در نوشتن کد تکراری وجود ندارد—حتی ممکن است خواناتر باشد. اما گاهی ممکن است بخواهید کد را خلاصه کنید. برخی از کامپوننتها تمام پراپهای خود را به فرزندانشان منتقل میکنند، مانند کاری که Profile در مثال بالا که با Avatar انجام میدهد. از آنجا که Profile هیچ یک از پراپهای خود را مستقیماً استفاده نمیکند، منطقی است که ورودی تجزیه نشده و از دستور "گسترش" برای پاس دادن ورودی ها به Avatar استفاده کنید:
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}
این کار تمام پراپهای Profile را به Avatar، پاس می دهد.
از دستور گسترش با احتیاط استفاده کنید. اگر کامپوننت های زیادی از پروژه از آن استفاده می کنید، به نظر می رسد چیزی اشتباه است. این کار، هشداری برای تقسیم کردن کامپوننت های پروژه شما است و شاید نیاز به ارسال فرزندان بعنوان ورودی به کامپوننتها مورد نظر داشته باشد.
ارسال JSX به عنوان فرزندان
تو در تو کردن تگهای HTML کار راحتی است:
<div>
<img />
</div>
برای کامپوننتهای React نیز می توانید کاری مشابه کد زیر انجام دهید:
<Card>
<Avatar />
</Card>
زمانی که محتوایی را درون تگ JSX تو در تو میکنید، کامپوننت والد آن را در پراپی به نام children دریافت میکند. به عنوان مثال، کامپوننت Card در کد زیر پراپی به نام children دریافت میکند که با <Avatar /> ست شده و آن را در یک تگ div رندر میکند:
مشاهده نتیجه کد زیر در CodeSandBox
import Avatar from './Avatar.js';
function Card({ children }) {
return (
<div className="card">
{children}
</div>
);
}
export default function Profile() {
return (
<Card>
<Avatar
size={100}
person={{
name: 'Katsuko Saruhashi',
imageId: 'YfeOqp2'
}}
/>
</Card>
);
}
import { getImageUrl } from './utils.js';
export default function Avatar({ person, size }) {
return (
<img
className="avatar"
src={getImageUrl(person)}
alt={person.name}
width={size}
height={size}
/>
);
}
export function getImageUrl(person, size = 's') {
return (
'https://i.imgur.com/' +
person.imageId +
size +
'.jpg'
);
}
سعی کنید <Avatar> داخل <Card> را با مقداری متن جایگزین کنید تا ببینید چگونه کامپوننت Card میتواند هر محتوایی را دریافت کند. این کامپوننت نیازی به "دانستن" چه چیزی درون آن رندر میشود، ندارد. این الگوی منعطف را در جاهای زیادی خواهید دید.
میتوانید کامپوننتی با پراپ children را مانند "قابی خالی" تصور کنید که میتواند توسط کامپوننتهای والد خود با JSX دلخواه "پر" شود. اغلب از ورودی children برای ظرف های بصری مانند پنلها، شبکهها و غیره استفاده خواهید کرد.

چگونه پراپها در طول زمان تغییر میکنند
کامپوننت Clock در کد زیر دو ورودی از کامپوننت والد خود دریافت میکند: color و time. (کد کامپوننت والد بدلیل استفاده از state و چون هنوز به آن نپرداخته ایم، حذف شده است .)
سعی کنید رنگ متن باکس انتخاب را CodeSandBox تغییر دهید:
export default function Clock({ color, time }) {
return (
<h1 style={{ color: color }}>
{time}
</h1>
);
}
import { useState, useEffect } from 'react';
import Clock from './Clock.js';
function useTime() {
const [time, setTime] = useState(() => new Date());
useEffect(() => {
const id = setInterval(() => {
setTime(new Date());
}, 1000);
return () => clearInterval(id);
}, []);
return time;
}
export default function App() {
const time = useTime();
const [color, setColor] = useState('lightcoral');
return (
<div>
<p>
Pick a color:{' '}
<select value={color} onChange={e => setColor(e.target.value)}>
<option value="lightcoral">lightcoral</option>
<option value="midnightblue">midnightblue</option>
<option value="rebeccapurple">rebeccapurple</option>
</select>
</p>
<Clock color={color} time={time.toLocaleTimeString()} />
</div>
);
}
این مثال نشان میدهد که چگونه یک کامپوننت ممکن است در طول زمان ورودی های مختلفی دریافت کند. پراپها همیشه ایستا نیستند! در اینجا، ورودی time هر ثانیه تغییر میکند، و ورودی color با تغییر رنگ، تغییر می کند. پراپها دادههای کامپوننت را در هر لحظه از زمان منعکس میکنند، نه فقط در ابتدا.
با این حال، پراپها غیرقابل تغییر هستند—اصطلاحی از علوم کامپیوتری به معنای "غیرقابل تغییر". هنگامی که یک کامپوننت نیاز به تغییر پراپهای خود دارد (برای مثال، در پاسخ به یک تعامل کاربر یا دادههای جدید)، باید از کامپوننت والد خود بخواهد که پراپهای متفاوتی به آن ارسال کند—یک شی جدید! پراپهای قدیمیتر کنار گذاشته می شوند و در نهایت موتور جاوااسکریپت حافظهای که توسط آنها اشغال شده را بازپس میگیرد.
سعی نکنید "پراپها را تغییر دهید". هنگامی که نیاز به پاسخ به ورودی کاربر دارید (مانند تغییر رنگ انتخاب شده)، باید "مقدار وضعیت را ست کنید". بای مطالعه بیشتر در مورد وضعیتها، مطلب وضعیت: حافظه کامپوننت مطالعه کنید.
جمع بندی
- برای ارسال پراپها، آنها را مانند پاس دادن ویژگیهای HTML به JSX اضافه کنید.
- برای خواندن پراپها، از دستور تجزیه
function Avatar({ person, size })در ورودی تابع کامپوننت استفاده کنید. - میتوانید یک مقدار پیشفرض به صورت
size = 100تعریف کنید که برای پراپهای ناموجود وundefinedاستفاده میشود. - میتوانید با دستور گسترش
<Avatar {...props} />تمام پراپهای والد را به فرزند ارسال کنید، اما از آن زیاد استفاده نکنید! - JSX تو در تو مانند
<Card><Avatar /></Card>به عنوان پراپchildrenکامپوننتCardظاهر میشود. - پراپها مقادیر فقط خواندی در هر لحظه از زمان هستند: هر رندر یک نسخه جدید از پراپها دریافت میکند.
- نمیتوانید پراپها را تغییر دهید. زمانی که نیاز به تعامل دارید، باید وضعیت را تغیین کنید.
چالش ها
استخراج یک کامپوننت
کامپوننت Gallery شامل برخی قسمتهای بسیار مشابه برای دو پروفایل است. برای کم کردن تکرار، کامپوننت Profile را از آن استخراج کنید. سپس پراپهایی که باید به این کامپوننت ارسال می کنید را انتخاب کنید.
راهنمایی: با استخراج مارکاپ مربوط به اطلاعات یک دانشمند شروع کنید. سپس تفاوت های بین آن ها را پیدا کرده و بعنوان ورودی به کامپوننت ارسال کنید.
کد را CodeSandBox باز کرده و چالش را حل کنید
تنظیم اندازه تصویر بر اساس پراپ
در این مثال، Avatar یک ورودی عددی size دریافت میکند که عرض و ارتفاع <img> را تعیین میکند. پراپ size در این مثال به 40 تنظیم میشود. با این حال، اگر تصویر را در یک برگه جدید باز کنید، خواهید دید که خود تصویر بزرگتر از این مقدار است (160 پیکسل). اندازه واقعی تصویر بر اساس اندازه ای که شما درخواست میکنید تعیین میشود.
کامپوننت Avatar را به شکلی تغییر دهید که بر اساس ورودی size، نزدیکترین اندازه تصویر را انتخاب کند. مثلن، اگر size کمتر از 90 باشد، 's' ("کوچک") را به جای 'b' ("بزرگ") به تابع getImageUrl ارسال کنید. برای بررسی صحت کد چند آواتار با مقادیر مختلف size رندر کنید.
کد را CodeSandBox باز کرده و چالش را حل کنید
ارسال JSX در ورودی children
کامپوننت Card را از کد زیر استخراج کرده و از طریق ورودی children برای ارسال JSX متفاوت به آن استفاده کنید:
راهنمایی: هر کد JSX ای که شما درون تگ کامپوننت قرار دهید، به عنوان پراپ children به آن کامپوننت ارسال میشود.
کد را CodeSandBox باز کرده و چالش را حل کنید
راه حل چالشها
چالش اول
در این راهحل، کامپوننت Profile چندین پراپ را دریافت میکند: imageId (یک رشته)، name (یک رشته)، profession (یک رشته)، awards (یک آرایه از رشتهها)، discovery (یک رشته) و imageSize (یک عدد).
توجه داشته باشید که پراپ imageSize یک مقدار پیشفرض دارد، به همین دلیل است که آن را به کامپوننت ارسال نمیکنیم.
import { getImageUrl } from './utils.js';
function Profile({
imageId,
name,
profession,
awards,
discovery,
imageSize = 70
}) {
return (
<section className="profile">
<h2>{name}</h2>
<img
className="avatar"
src={getImageUrl(imageId)}
alt={name}
width={imageSize}
height={imageSize}
/>
<ul>
<li><b>Profession:</b> {profession}</li>
<li>
<b>Awards: {awards.length} </b>
({awards.join(', ')})
</li>
<li>
<b>Discovered: </b>
{discovery}
</li>
</ul>
</section>
);
}
export default function Gallery() {
return (
<div>
<h1>Notable Scientists</h1>
<Profile
imageId="szV5sdG"
name="Maria Skłodowska-Curie"
profession="physicist and chemist"
discovery="polonium (chemical element)"
awards={[
'Nobel Prize in Physics',
'Nobel Prize in Chemistry',
'Davy Medal',
'Matteucci Medal'
]}
/>
<Profile
imageId='YfeOqp2'
name='Katsuko Saruhashi'
profession='geochemist'
discovery="a method for measuring carbon dioxide in seawater"
awards={[
'Miyake Prize for geochemistry',
'Tanaka Prize'
]}
/>
</div>
);
}
توجه کنید که اگر awards یک آرایه باشد، نیازی به پراپ جداگانه awardCount نیست. در این صورت میتوانید از awards.length برای شمارش تعداد جوایز استفاده کنید. به یاد داشته باشید که پراپها میتوانند هر مقداری را بپذیرند و این شامل آرایهها نیز میشود!
راهحل دیگری که شباهت بیشتری به مثالهای قبلی در این صفحه دارد، این است که تمام اطلاعات یک شخص را در یک شیء واحد گروهبندی کرده و آن شیء را به عنوان یک پراپ ارسال کنید:
import { getImageUrl } from './utils.js';
function Profile({ person, imageSize = 70 }) {
const imageSrc = getImageUrl(person)
return (
<section className="profile">
<h2>{person.name}</h2>
<img
className="avatar"
src={imageSrc}
alt={person.name}
width={imageSize}
height={imageSize}
/>
<ul>
<li>
<b>Profession:</b> {person.profession}
</li>
<li>
<b>Awards: {person.awards.length} </b>
({person.awards.join(', ')})
</li>
<li>
<b>Discovered: </b>
{person.discovery}
</li>
</ul>
</section>
)
}
export default function Gallery() {
return (
<div>
<h1>Notable Scientists</h1>
<Profile person={{
imageId: 'szV5sdG',
name: 'Maria Skłodowska-Curie',
profession: 'physicist and chemist',
discovery: 'polonium (chemical element)',
awards: [
'Nobel Prize in Physics',
'Nobel Prize in Chemistry',
'Davy Medal',
'Matteucci Medal'
],
}} />
<Profile person={{
imageId: 'YfeOqp2',
name: 'Katsuko Saruhashi',
profession: 'geochemist',
discovery: 'a method for measuring carbon dioxide in seawater',
awards: [
'Miyake Prize for geochemistry',
'Tanaka Prize'
],
}} />
</div>
);
}
اگرچه سینتکس بدلیل پاس دادن شی به کامپوننت Profile کمی متفاوت به نظر میرسد اما این دو مثال کارکرد و خروجی یکسانی دارند و میتوانید هر رویکرد دیگری غیر از این را نیز انتخاب کنید.
چالش دوم
چگونگی حل این چالش به صورت زیر است:
import { getImageUrl } from './utils.js';
function Avatar({ person, size }) {
let thumbnailSize = 's';
if (size > 90) {
thumbnailSize = 'b';
}
return (
<img
className="avatar"
src={getImageUrl(person, thumbnailSize)}
alt={person.name}
width={size}
height={size}
/>
);
}
export default function Profile() {
return (
<>
<Avatar
size={40}
person={{
name: 'Gregorio Y. Zara',
imageId: '7vQD0fP'
}}
/>
<Avatar
size={120}
person={{
name: 'Gregorio Y. Zara',
imageId: '7vQD0fP'
}}
/>
</>
);
}
همچنین با در نظر گرفتن window.devicePixelRatio می توانید یک تصویر با وضوح بالاتر برای صفحات با DPI بالا نمایش دهید:
import { getImageUrl } from './utils.js';
const ratio = window.devicePixelRatio;
function Avatar({ person, size }) {
let thumbnailSize = 's';
if (size * ratio > 90) {
thumbnailSize = 'b';
}
return (
<img
className="avatar"
src={getImageUrl(person, thumbnailSize)}
alt={person.name}
width={size}
height={size}
/>
);
}
export default function Profile() {
return (
<>
<Avatar
size={40}
person={{
name: 'Gregorio Y. Zara',
imageId: '7vQD0fP'
}}
/>
<Avatar
size={70}
person={{
name: 'Gregorio Y. Zara',
imageId: '7vQD0fP'
}}
/>
<Avatar
size={120}
person={{
name: 'Gregorio Y. Zara',
imageId: '7vQD0fP'
}}
/>
</>
);
}
با پراپها می توانید منطق مشابهی را درون کامپوننت Avatar ایجاد کنید (و در صورت نیاز بعداً آن را تغییر دهید) تا همه بتوانند از کامپوننت <Avatar> استفاده کنند بدون اینکه در مورد نحوه درخواست و تغییر اندازه تصاویر چیزی بدانند.
چالش سوم
نحوه استفاده از کامپوننت Card به صورت زیر است:
function Card({ children }) {
return (
<div className="card">
<div className="card-content">
{children}
</div>
</div>
);
}
export default function Profile() {
return (
<div>
<Card>
<h1>Photo</h1>
<img
className="avatar"
src="https://i.imgur.com/OKS67lhm.jpg"
alt="Aklilu Lemma"
width={100}
height={100}
/>
</Card>
<Card>
<h1>About</h1>
<p>Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.</p>
</Card>
</div>
);
}
همچنین title را می توانید به یک پراپ جداگانه تبدیل کنید تا هر Card یک عنوان برای خود داشته باشد:
function Card({ children, title }) {
return (
<div className="card">
<div className="card-content">
<h1>{title}</h1>
{children}
</div>
</div>
);
}
export default function Profile() {
return (
<div>
<Card title="Photo">
<img
className="avatar"
src="https://i.imgur.com/OKS67lhm.jpg"
alt="Aklilu Lemma"
width={100}
height={100}
/>
</Card>
<Card title="About">
<p>Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.</p>
</Card>
</div>
);
}