ارسال داده ها به یک کامپوننت در 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>
);
}