نمایش مشروط در React
کامپوننت ها اغلب بسته به شرایط مختلف نیازمند نمایش بخش های متفاوتی هستند. در React، میتوانید JSX را به طور شرطی با استفاده از سینتکس جاوا اسکریپت مانند عبارات if، && و اپراتورهای ? : رندر کنید.
بازگشت شرطی JSX
فرض کنید شما یک کامپوننت PackingList دارید که چندین Item را رندر میکند که میتوانند به عنوان بسته بندی شده یا نشده نمایش داده شوند:
function Item({ name, isPacked }) {
return <li className="item">{name}</li>;
}
export default function PackingList() {
return (
<section>
<h1>Sally Ride's Packing List</h1>
<ul>
<Item
isPacked={true}
name="Space suit"
/>
<Item
isPacked={true}
name="Helmet with a golden leaf"
/>
<Item
isPacked={false}
name="Photo of Tam"
/>
</ul>
</section>
);
}
توجه داشته باشید که برخی از اجزای Item، خاصیت isPacked آنها به جای false به true تنظیم شده است. حال میخواهید برای اقلام بسته بندی شده (اگر isPacked={true} باشد) یک تیک (✅) نمایش دهید.
می توانید این کار را به صورت یک عبارت بنویسید:
if (isPacked) {
return <li className="item">{name} ✅</li>;
}
return <li className="item">{name}</li>;
اگر خاصیت isPacked برابر با true باشد، یک درخت JSX متفاوت را بر میگرداند. با این تغییر، برخی از اقلام در انتها یک تیک میگیرند:
function Item({ name, isPacked }) {
if (isPacked) {
return <li className="item">{name} ✅</li>;
}
return <li className="item">{name}</li>;
}
export default function PackingList() {
return (
<section>
<h1>Sally Ride's Packing List</h1>
<ul>
<Item
isPacked={true}
name="Space suit"
/>
<Item
isPacked={true}
name="Helmet with a golden leaf"
/>
<Item
isPacked={false}
name="Photo of Tam"
/>
</ul>
</section>
);
}
سعی کنید آنچه در هر حالت برگردانده میشود را در CodeSandBox ویرایش کنید و ببینید که نتیجه چگونه تغییر میکند!
توجه کنید که چگونه شاخه منطقی را با دستورات if و return جاوا اسکریپت ایجاد میکنید. در React، جریان کنترلی (مانند شرایط) توسط جاوا اسکریپت مدیریت میشود.
بازگشت شرطی هیچ چیز با null
در برخی شرایط، ممکن است نخواهید چیزی را رندر کنید. به عنوان مثال، فرض کنید که نمیخواهید اقلام بسته بندی شده را نشان دهید. اما یک کامپوننت باید چیزی را برگرداند. در این مورد، میتوانید null برگردانید:
if (isPacked) {
return null;
}
return <li className="item">{name}</li>;
اگر isPacked برابر با true باشد، کامپوننت هیچ چیزی برنمیگرداند، null. در غیر این صورت، عبارت JSX را برای رندر برمیگرداند.
function Item({ name, isPacked }) {
if (isPacked) {
return null;
}
return <li className="item">{name}</li>;
}
export default function PackingList() {
return (
<section>
<h1>Sally Ride's Packing List</h1>
<ul>
<Item
isPacked={true}
name="Space suit"
/>
<Item
isPacked={true}
name="Helmet with a golden leaf"
/>
<Item
isPacked={false}
name="Photo of Tam"
/>
</ul>
</section>
);
}
در عمل، بازگشت null از یک کامپوننت رایج نیست زیرا ممکن است توسعهدهندهای که سعی در رندر آن دارد را شگفتزده کند. اغلب ، ممکن است بخواهید کامپوننت را به طور شرطی در کامپوننت والد نمایش دهید یا ندهید.
شامل کردن شرطی JSX
در مثال قبلی، کنترل برگرداندن درخت JSX (اگر وجود داشته باشد!) مورد نظر را انجام دادید. ممکن است در این بین متوجه نمایش مقادیر تکرار در خروجی شده باشید:
<li className="item">{name} ✅</li>
بسیار شبیه به کد زیر است
<li className="item">{name}</li>
هر دو شاخه شرطی عبارت <li className="item">...</li> را برمیگردانند:
if (isPacked) {
return <li className="item">{name} ✅</li>;
}
return <li className="item">{name}</li>;
در حالی که این تکرار مضر نیست، میتواند نگهداری کد را در آینده برای شما و سایر برنامه نویسان سختتر کند. مثلن اگر بخواهید className را تغییر دهید چه؟ مجبورید این کار را در دو جای کد انجام دهید! در چنین شرایطی، به طور شرطی میتوانید JSX کوچکی را وارد کد خود کنید تا کد شما قانون DRY را رعایت کند.
اپراتور شرطی (ترینری) (? :)
جاوا اسکریپت یک سینتکس فشرده برای نوشتن یک عبارت شرطی دارد - اپراتور شرطی یا "اپراتور ترینری".
به جای این کد:
if (isPacked) {
return <li className="item">{name} ✅</li>;
}
return <li className="item">{name}</li>;
میتوانید این کد را بنویسید:
return (
<li className="item">
{isPacked ? name + ' ✅' : name}
</li>
);
شما میتوانید آن به صورت "اگر بخوانید.
آیا این دو مثال کاملاً معادل هستند؟
اگر شما پیش زمینه برنامهنویسی شیگرا دارید، ممکن است فرض کنید که دو مثال بالا به طور ظریفی متفاوت هستند زیرا یکی از آنها ممکن است دو "نمونه" مختلف از <li> ایجاد کند. اما عناصر JSX "نمونه" نیستند زیرا هیچ حالت داخلی ندارند و نودهای واقعی DOM نیستند. آنها توصیفهای سبک وزنی مانند نقشهها (blueprints) هستند. بنابراین این دو مثال در واقع کاملاً معادل هستند. حفظ و بازنشانی وضعیت جزئیات بیشتری درباره چگونگی این موضوع به شما میدهد.
حالا فرض کنید میخواهید متن آیتم کامل شده را درون یک تگ HTML دیگر، مانند <del> برای خط زدن، قرار دهید. میتوانید خطوط جدید و پرانتزهای بیشتری اضافه کنید تا راحتتر بتوانید دستورات JSX بیشتری را در هر یک از موارد درج کنید:
function Item({ name, isPacked }) {
return (
<li className="item">
{isPacked ? (
<del>
{name + ' ✅'}
</del>
) : (
name
)}
</li>
);
}
export default function PackingList() {
return (
<section>
<h1>Sally Ride's Packing List</h1>
<ul>
<Item
isPacked={true}
name="Space suit"
/>
<Item
isPacked={true}
name="Helmet with a golden leaf"
/>
<Item
isPacked={false}
name="Photo of Tam"
/>
</ul>
</section>
);
}
مشاهده کد و نتیجه در CodeSandBox
این سبک برای شرایط ساده کار میکند، اما به اندازه نیاز از آن استفاده کنید. اگر به خاطر تودرتو کردن بیش از حد بودن کدهای شما نامرتب شوند، می توانید فرزندان را به کامپوننت های مختص خودشان یا به متغیری خارج از JSX بازگشتی اختصاص دهید. در React، نشانهگذاری بخشی از کد شما است، بنابراین میتوانید از ابزارهایی مانند متغیرها و توابع برای تمیز کردن عبارات پیچیده استفاده کنید.
اپراتور منطقی AND (&&)
میانبر رایج دیگری که با آن مواجه خواهید شد، اپراتور منطقی AND جاوا اسکریپت ( است. داخل کامپوننتهای React، این معمولاً زمانی پیش میآید که میخواهید برخی از JSXها را وقتی شرط true است رندر کنید، یا در غیر این صورت هیچ چیزی رندر نکنید. با اپراتور &&، میتوانید فقط در صورتی که isPacked برابر با true باشد تیک را رندر کنید:
return (
<li className="item">
{name} {isPacked && '✅'}
</li>
);
این عبارت را میتوانید به صورت "اگر بخوانید.
اینجا در عمل آن را مشاهده کنید:
function Item({ name, isPacked }) {
return (
<li className="item">
{name} {isPacked && '✅'}
</li>
);
}
export default function PackingList() {
return (
<section>
<h1>Sally Ride's Packing List</h1>
<ul>
<Item
isPacked={true}
name="Space suit"
/>
<Item
isPacked={true}
name="Helmet with a golden leaf"
/>
<Item
isPacked={false}
name="Photo of Tam"
/>
</ul>
</section>
);
}
عبارت && جاوا اسکریپت ارزش سمت راست خود را در صورتی سمت چپ (شرط ما) برابر با true باشد، برمیگرداند. اما اگر شرط false باشد، کل عبارت به false تبدیل میشود. React مقدار false را به عنوان یک "حفره" در درخت JSX در نظر میگیرد، درست مانند null یا undefined، و در جای آن هیچ چیزی رندر نمیکند.
عددها را در سمت چپ عملگر
برای تست شرط، جاوا اسکریپت به طور خودکار سمت چپ را به یک boolean تبدیل میکند. با این حال، اگر سمت چپ 0 باشد، سپس کل عبارت آن مقدار (0) را دریافت میکند و React مقدار 0 را به جای هیچ رندر میکند.
به عنوان مثال، یک اشتباه رایج نوشتن کدی مانند messageCount && <p>New messages</p> است. فرض وقتی messageCount برابر با 0 آسان است، شاید فکر کنید هیچ چیزی رندر نمیشود، اما در واقع React خود 0 را رندر میکند!
برای اصلاح آن، سمت چپ را با مقداری boolean چایگزین کنید: messageCount > 0 && <p>New messages</p>.
مقدار دهی شرطی به یک متغیر و استفاده از آن در JSX
هنگامی که میانبرها مانع نوشتن کد ساده میشوند، سعی کنید از عبارت if و یک متغیر استفاده کنید. شما میتوانید متغیرهای تعریف شده با let را دوباره تنظیم کنید، پس با ارائه محتوای پیشفرضی که میخواهید نمایش دهید، شروع کنید:
let itemContent = name;
سپس از یک عبارت if برای ارجاع دوباره یک عبارت JSX به itemContent در صورت true بودن isPacked استفاده کنید:
if (isPacked) {
itemContent = name + " ✅";
}
متغیر را با آکولادها در درخت JSX بازگشتی بگنجانید تا عبارت محاسبه شده درون متغیر را در داخل JSX نمایش دهد:
<li className="item">
{itemContent}
</li>
این سبک کد بیشتری نسبت به روش های دیگر دارد، اما همچنین انعطافپذیرترین روش است:
function Item({ name, isPacked }) {
let itemContent = name;
if (isPacked) {
itemContent = name + " ✅";
}
return (
<li className="item">
{itemContent}
</li>
);
}
export default function PackingList() {
return (
<section>
<h1>Sally Ride's Packing List</h1>
<ul>
<Item
isPacked={true}
name="Space suit"
/>
<Item
isPacked={true}
name="Helmet with a golden leaf"
/>
<Item
isPacked={false}
name="Photo of Tam"
/>
</ul>
</section>
);
}
مانند قبل، این روش نه تنها برای متن، بلکه برای JSX دلخواه نیز کار میکند:
function Item({ name, isPacked }) {
let itemContent = name;
if (isPacked) {
itemContent = (
<del>
{name + " ✅"}
</del>
);
}
return (
<li className="item">
{itemContent}
</li>
);
}
export default function PackingList() {
return (
<section>
<h1>Sally Ride's Packing List</h1>
<ul>
<Item
isPacked={true}
name="Space suit"
/>
<Item
isPacked={true}
name="Helmet with a golden leaf"
/>
<Item
isPacked={false}
name="Photo of Tam"
/>
</ul>
</section>
);
}
اگر با جاوا اسکریپت آشنا نیستید، این تنوع از سبکها ممکن است در ابتدا برایتان سخت به نظر بیاید. با این حال، یادگیری آنها به شما کمک میکند تا هر کد جاوا اسکریپت را بتوانید بخوانید و بنویسید - نه فقط کامپوننت React! یکی از روش ها را که برای شروع ترجیح میدهید انتخاب کنید و اگر سایر روش ها را فراموش کردید، دوباره به این مطلب مراجعه کنید.
جمع بندی
- در React، شما منطق branching را با جاوا اسکریپت کنترل میکنید.
- شما میتوانید یک عبارت JSX را به طور شرطی با یک عبارت
ifبرگردانید. - یک JSX را میتوانید به طور شرطی در یک متغیر قرار دهید، سپس بوسیله آکولادها داخل JSX نمایش دهید.
- در JSX، عبارت
{cond ? <A /> : <B />}به معنی "اگر مقدار است. - در JSX، عبارت
{cond && <A />}به این معنی "اگر مقدار است. - میانبرها رایج هستند، اما اگر شما سبک ساده
ifرا ترجیح میدهید نیازی به استفاده از آنها نیست.
چالش ها
1. نمایش یک آیکون برای اقلام ناقص با ? :
از اپراتور شرطی (cond ? a : b) برای رندر علامت ❌ در صورتی که مقدار isPacked برابر با true نیست استفاده کنید.
برای مشاهده و حل چالش در CodeSandBox کلیک کنید
2. نمایش اهمیت آیتم با &&
در این مثال، هر Item یک ویژگی عددی به نام importance دریافت میکند. از اپراتور && برای رندر "(اهمیت: X)" به صورت ایتالیک استفاده کنید، اما فقط برای اقلامی که اهمیت مقداری غیر صفر دارند. در نهایت لیست اقلام شما باید به این شکل درآید:
- لباس فضایی (اهمیت: 9)
- کلاهی با برگ طلایی
- عکس تام (اهمیت: 6)
فراموش نکنید که یک فاصله بین دو تگ label اضافه کنید!
3. باز نویسی سری ای از ? : به if و متغیرها
کامپوننت Drink از یک سری عبارت شرطی ? : استفاده میکند تا اطلاعات مختلفی را بسته به اینکه خاصیت name برابر با "tea" یا "coffee" است، نشان دهد. مشکل این است که اطلاعات هر نوشیدنی در میان چندین شرط پخش شده است. این کد را بازنویسی کنید تا از یک عبارت if واحد به جای سه شرط ? : استفاده کنید.
پس از اینکه کد را به گونه ای بازسازی کردید که از if استفاده کند، آیا ایدههای بیشتری برای سادهسازی بیشتر کد دارید؟
راه حل چالش ها
چالش اول
چالش دوم
برای حل این چالش باید به روش زیر عمل کنید:
function Item({ name, importance }) {
return (
<li className="item">
{name}
{importance > 0 && ' '}
{importance > 0 &&
<i>(Importance: {importance})</i>
}
</li>
);
}
export default function PackingList() {
return (
<section>
<h1>Sally Ride's Packing List</h1>
<ul>
<Item
importance={9}
name="Space suit"
/>
<Item
importance={0}
name="Helmet with a golden leaf"
/>
<Item
importance={6}
name="Photo of Tam"
/>
</ul>
</section>
);
}
توجه داشته باشید که برای رندر کردن اهمیت باید عبارت importance > 0 && ... را به جای importance && ... بنویسید تا اگر importance برابر با 0 باشد، 0 به عنوان نتیجه رندر نشود!
در این راهحل، دو شرط جداگانه برای وارد کردن فاصله بین نام و برچسب اهمیت استفاده میشود. به طور جایگزین، میتوانید از یک Fragment با فاصله اولیه استفاده کنید: importance > 0 && <> <i>...</i></> یا فاصله را بلافاصله درون <i> اضافه کنید: importance > 0 && <i> ...</i>.
چالش سوم
روشهای زیادی وجود دارد که میتوانید این چالش را حل کنید، اما یکی از روش ها به صورت زیر است:
function Drink({ name }) {
let part, caffeine, age;
if (name === 'tea') {
part = 'leaf';
caffeine = '15–70 mg/cup';
age = '4,000+ years';
} else if (name === 'coffee') {
part = 'bean';
caffeine = '80–185 mg/cup';
age = '1,000+ years';
}
return (
<section>
<h1>{name}</h1>
<dl>
<dt>Part of plant</dt>
<dd>{part}</dd>
<dt>Caffeine content</dt>
<dd>{caffeine}</dd>
<dt>Age</dt>
<dd>{age}</dd>
</dl>
</section>
);
}
export default function DrinkList() {
return (
<div>
<Drink name="tea" />
<Drink name="coffee" />
</div>
);
}
در اینجا اطلاعات هر نوشیدنی به جای پراکنده شدن در میان چندین شرط، با هم گروهبندی شده است . این کار افزودن نوشیدنیهای بیشتر را آسانتر میکند.
یک راهحل دیگر این است که شرط را به طور کلی با قرار دادن اطلاعات نوشیدنی ها درون اشیا حذف کنید:
const drinks = {
tea: {
part: 'leaf',
caffeine: '15–70 mg/cup',
age: '4,000+ years'
},
coffee: {
part: 'bean',
caffeine: '80–185 mg/cup',
age: '1,000+ years'
}
};
function Drink({ name }) {
const info = drinks[name];
return (
<section>
<h1>{name}</h1>
<dl>
<dt>Part of plant</dt>
<dd>{info.part}</dd>
<dt>Caffeine content</dt>
<dd>{info.caffeine}</dd>
<dt>Age</dt>
<dd>{info.age}</dd>
</dl>
</section>
);
}
export default function DrinkList() {
return (
<div>
<Drink name="tea" />
<Drink name="coffee" />
</div>
);
}