نمایش مشروط در 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>
);
}