هر چیزی که در مورد رندر کردن شرطی در ری اکت باید بدانیم!

وقتی کار با شرط ها رو شروع می‌کنیم، کدنویسی جالب‌تر می‌شه، به عنوان مثال، اگر بخواهیم برخی از مؤلفه‌ها را تنها زمانی که شرایط خاصی مطابقت دارند، رندر کنیم. این واقعیت که ما می‌تونیم از جاوا اسکریپت در JSX ری اکت استفاده کنیم خودش یک مزیت بزرگ حساب میشه، اما راه‌های مختلفی برای بیان شرط در JSX وجود دارد، و درک مزایا و مشکلات هر یک از این‌ها برای نوشتن کدی که خوانا باشد، مهم است. فرض کنید می‌خواهیم دکمه خروج را تنها در صورتی نشان دهیم که کاربر در حال حاضر به سیستم ما وارد شده باشد.یک کد ساده برای شروع به شرح زیر است:let button if (isLoggedIn) { button =<LogoutButton /> } return <div>{button}</div>این کد کار می کند، اما اصلا خوانا نیست، به خصوص اگر چندین کامپوننت و شرط های مختلفی وجود داشته باشد. در JSX می توانیم از یک شرط درون خطی (inline condition) استفاده کنیم:<div> {isLoggedIn && <LoginButton />} </div> اگر شرط false باشد، هیچ چیز رندر نمی‌شود، اما اگر شرط درست باشد، تابع LoginButton فراخوانی می‌شود و نتیجه برگردانده می‌شود. اگر شرط یک جایگزین داشته باشد (if...else) و ما بخواهیم، به عنوان مثال، برای نشان دادن دکمه خروج در صورت ورود کاربر و دکمه ورود در غیر این صورت، می توانیم از عبارت if...else جاوا اسکریپت به صورت زیر استفاده کنیم: let button if (isLoggedIn) { button = <LogoutButton /> } else { button = <LoginButton /> } return <div>{button}</div>یه روش دیگه، و حتی بهتر، می‌تونیم از یک شرط سه‌گانه (ternary condition) استفاده کنیم که کد رو فشرده‌تر می‌کنه:<div> {isLoggedIn ? <LogoutButton /> : <LoginButton />} </div>بیایید اکنون بهترین راه حل را برای زمانی که همه چیز پیچیده تر می شود و، برای مثال، ما باید بیش از یک متغیر را بررسی کنیم تا مشخص کنیم که آیا یک جزء را رندر کنیم یا نه، بررسی کنیم:<div> {dataIsReady && (isAdmin || userHasPermissions) && <SecretData /> } </div>در این مورد، واضح است که استفاده از شرط درون خطی راه حل خوبی است، اما خوانایی به شدت تحت تاثیر قرار می گیرد. در عوض، می‌توانیم یک تابع کمکی در داخل کامپوننت خود ایجاد کنیم و از آن در JSX برای اعمال کردن شرایط استفاده کنیم:const canShowSecretData = () => { const { dataIsReady, isAdmin, userHasPermissions } = props return dataIsReady && (isAdmin || userHasPermissions) } return ( <div> {this.canShowSecretData() && <SecretData />} </div> ) همانطور که می بینید، این تغییر کد را خواناتر و شرط ها را واضح تر می کند.همین امر در مورد خصوصیات محاسبه شده (computed properties) نیز صدق می کند. فرض کنید دو ملک واحد برای ارز و ارزش دارید. به جای ایجاد رشته قیمت در داخل رندر، می توانید یک تابع ایجاد کنید:const getPrice = () => { return `${props.currency}${props.value}` } return <div>{getPrice()}</div> این مدل نوشتن بهتر است چون که هم ایزوله ست و هم اگر منطقی داشته باشد به راحتی می توانید آن را تست کنید.همانند جاوا اسکریپت میتوانیم از Switch Case نیز در jsx ری اکت استفاده کنیمfunction Notification({ param }) { return ( <div> {(function() { switch(param) { case 'foo': return 'bar'; default: return 'foo'; } } })()} </div> );} توجه داشته باشید که همیشه باید از default برای عملگر switch caseاستفاده کنید، زیرا در React، یک کامپوننت همیشه باید یک عنصر یا null را برگرداند.برای تمیزتر کردن آن، می‌توانیم سوئیچ را در یک تابع از رندر خارج کنیم و فقط آن را با عبور پارامترهای مورد نظر خود فراخوانی کنیم. مثال زیر را ببینید.renderSwitch(param) { switch(param) { case 'foo': return 'bar'; default: return 'foo'; }}render() { return ( <div> {this.renderSwitch(param)} <div> );}در یه مثال دیگ میخوایم از enum ها در شرطی کردن رندر ها استفاده کنیم.ما می خواهیم سه کامپوننت مختلف Foo، Bar و Default ایجاد کنیم و این کامپوننت ها را بر اساس یک state خاص نشان دهیم. const Foo = () => { return <button>FOO</button>;};const Bar = () => { return <button>BAR</button>;};const Default = () => { return <button>DEFAULT</button>;};اکنون یک شی ایجاد می کنیم که می تواند به عنوان enum استفاده شود.const ENUM_STATES = { foo: <Foo />, bar: <Bar />, default: <Default />}; بیایید اکنون تابعی ایجاد کنیم که حالت را به عنوان پارامتر بگیرد و کامپوننت را بر اساس "state" برگرداند. function EnumState({ state }) { return <div>{ENUM_STATES[state]}</div>;}بیایید یک کامپوننت Enum ایجاد کنیم که مقادیر "state" را به تابع "EnumState" منتقل می کند.class Enum extends React.Component { render() { return ( <div> <h1>Conditional Rendering with enums</h1> <EnumState state=&quotdefault&quot></EnumState> <EnumState state=&quotbar&quot></EnumState> <EnumState state=&quotfoo&quot></EnumState> </div> ); }} راه حل های دیگه ای هم هستند که نیاز به استفاده از وابستگی های خارجی دارند. یک تمرین خوب این است که تا آنجا که می‌توانیم از وابستگی‌های خارجی اجتناب کنیم تا bundle خود را کوچک‌تر نگه داریم، اما ممکن است در این مورد خاص ارزشش را داشته باشد زیرا بهبود خوانایی کدهای ما خودش یک پیروزی بزرگ است.راه حل اول استفاده از render-if است که می توانیم با کامند زیر آن را نصب کنیم:npm install --save render-ifسپس می توانیم به راحتی از آن در پروژه های خود استفاده کنیم:const { dataIsReady, isAdmin, userHasPermissions } = propsconst canShowSecretData = renderIf( dataIsReady && (isAdmin || userHasPermissions));return ( <div> {canShowSecretData(<SecretData />)} </div>);در اینجا، ما شرط های خود را در تابع renderIf قرار می دهیم.یک هدف این است که هرگز منطق زیادی کنار کامپوننت خود اضافه نکنیم. برخی از آنها به مقداری از آن نیاز دارند، اما ما باید سعی کنیم تا حد امکان آنها را ساده نگه داریم تا بتوانیم به راحتی خطاها را شناسایی و برطرف کنیم.حداقل باید سعی کنیم متد renderIf را تا حد امکان تمیز نگه داریم و برای انجام این کار، می‌توانیم از کتابخانه دیگری به نام react-only-if استفاده کنیم که با استفاده از کامپوننت های مرتبه بالا (HOC) کار میکند .برای استفاده از کتابخانه باید آن را با کامند زیر نصب کنیم:npm install --save react-only-ifپس از نصب، می توانیم از آن در برنامه های خود به روش زیر استفاده کنیم:import onlyIf from 'react-only-if'const SecretDataOnlyIf = onlyIf( ({ dataIsReady, isAdmin, userHasPermissions }) => dataIsReady && (isAdmin || userHasPermissions))(SecretData)const MyComponent = () => (<div> <SecretDataOnlyIf dataIsReady={...} isAdmin={...} userHasPermissions={...} /></div>)export default MyComponentهمانطور که در اینجا می بینید، هیچ منطقی در داخل خود کامپوننت وجود ندارد.شرط را به عنوان اولین پارامتر تابع onlyIf می‌دهیم و وقتی شرط مطابقت داشت، کامپوننت رندر می‌شود.تابعی که برای اعتبارسنجی شرط استفاده می‌شود، properties، state و context کامپوننت رو دریافت می‌کند.به این ترتیب از آلوده کردن کامپوننت خود به شرطی ها جلوگیری می کنیم تا درک و استدلال در مورد آن آسان تر باشد.اگر احساس می کنید که استفاده از JSX برای این نوع عملیات کد شما را خواناتر می کند، یک پلاگین Babel برای انجام این کار در دسترس است: jsx-control-statements.این از همان فلسفه JSX پیروی می کند و هیچ عملکرد واقعی به زبان اضافه نمی کند. بیایید ببینیم چگونه کار می کند.اول از همه باید آن را نصب کنیم:npm install --save jsx-control-statementsپس از نصب، باید آن را به لیست پلاگین های Babel خود در فایل babelrc. خود اضافه کنیم:&quotplugins&quot: [&quotjsx-control-statements&quot]از این به بعد، ما می توانیم از سینتکس ارائه شده توسط پلاگین استفاده کنیم و Babel آن را همراه با سینتکس رایج JSX ترانسپایل می کند.یک عبارت شرطی که با استفاده از پلاگین نوشته شده است، مثل قطعه زیر است:<If condition={this.canShowSecretData}> <SecretData /></If>این به یک عبارت شرطی سه گانه (ternary condition) که در بالا توضیح دادیم ترنسپایل می شود:{canShowSecretData ? <SecretData /> : null} اگر،به دلایلی، شما شرط های تودرتو در فانکشن رندر خود دارید، این به تنهایی میتواند باعث به هم ریختگی و ناخوانایی کد شما شود. اینجاست که کامپوننت Choose به کار می آید:<Choose> <When condition={...}> <span>if</span> </When> <When condition={...}> <span>else if</span> </When> <Otherwise> <span>else</span> </Otherwise></Choose> کد بالا به چندین عبارت شرطی سه گانه (ternary condition) تبدیل می شود.در آخر باید بگیم استفاده از مراحل بالا هر کدوم مزایای خاص خودشون رو دارن! امیدوارم تصمیم گیری در مورد استفاده هر کدوم رو براتون آسونتر کرده باشم و اطلاعات مفیدی رو در اختیارتون قرار داده باشم.منابع : https://flexiple.com/react/conditional-rendering-in-reactReact 17 Design Patterns and Best Practices Third Edition-Carlos Santana Roldán