جاوااسکریپت - نمایش یا مخفی کردن عنصر در ریاکت
من برای اولین بار با React.js کار میکنم و نمیتوانم راهی پیدا کنم که چیزی را در یک صفحه از طریق رویداد کلیک نشان دهم یا مخفی کنم. من هیچ کتابخانه دیگری را به صفحه اضافه نمیکنم، بنابراین دنبال یک راه بومی برای استفاده از کتابخانه React هستم. این چیزی است که تا الان دارم. من میخواهم زمانی که رویداد کلیک رخ میدهد، div نتایج را نشان دهم.
var Search= React.createClass({
handleClick: function (event) {
console.log(this.prop);
},
render: function () {
return (
<div className="date-range">
<input type="submit" value="Search" onClick={this.handleClick} />
</div>
);
}
});
var Results = React.createClass({
render: function () {
return (
<div id="results" className="search-results">
Some Results
</div>
);
}
});
React.renderComponent(<Search /> , document.body);
پاسخ شماره ۱
ریاکت حدود سال ۲۰۲۰
در callback onClick، تابع setter هوک state را برای بهروزرسانی state و بازرندر کردن مجدد صدا بزنید:
const Search = () => {
const [showResults, setShowResults] = React.useState(false)
const onClick = () => setShowResults(true)
return (
<div>
<input type="submit" value="Search" onClick={onClick} />
{ showResults ? <Results /> : null }
</div>
)
}
const Results = () => (
<div id="results" className="search-results">
Some Results
</div>
)
ReactDOM.render(<Search />, document.querySelector("#container"))<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>جیاسفیدل
ریاکت حدود سال ۲۰۱۴
کلید این است که وضعیت کامپوننت را در هندلر کلیک با استفاده از setState بهروزرسانی کنید. وقتی تغییرات وضعیت اعمال میشوند، متد render دوباره با وضعیت جدید فراخوانی میشود:
var Search = React.createClass({
getInitialState: function() {
return { showResults: false };
},
onClick: function() {
this.setState({ showResults: true });
},
render: function() {
return (
<div>
<input type="submit" value="Search" onClick={this.onClick} />
{ this.state.showResults ? <Results /> : null }
</div>
);
}
});
var Results = React.createClass({
render: function() {
return (
<div id="results" className="search-results">
Some Results
</div>
);
}
});
ReactDOM.render( <Search /> , document.getElementById('container'));<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/15.6.2/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>جیاسفیدل
پاسخ شماره ۲
<style type="text/css">
.hidden { display:none; }
</style>
const Example = props =>
<div className={props.shouldHide? 'hidden' : undefined}>Hello</div>
پاسخ شماره ۳
در اینجا یک نگارش جایگزین برای عملگر سهتایی وجود دارد:
{ this.state.showMyComponent ? <MyComponent /> : null }
معادل است با:
{ this.state.showMyComponent && <MyComponent /> }
بیاموزید چرا
همچنین نحو جایگزین با display: 'none';
<MyComponent style={this.state.showMyComponent ? {} : { display: 'none' }} />
با این حال، اگر از display: 'none' بیش از حد استفاده کنید، این منجر به آلودگی DOM میشود و در نهایت برنامه شما را کند میکند.
پاسخ شماره ۴
این روش من است.
import React, { useState } from 'react';
function ToggleBox({ title, children }) {
const [isOpened, setIsOpened] = useState(false);
function toggle() {
setIsOpened(wasOpened => !wasOpened);
}
return (
<div className="box">
<div className="boxTitle" onClick={toggle}>
{title}
</div>
{isOpened && (
<div className="boxContent">
{children}
</div>
)}
</div>
);
}
در کد بالا، برای رسیدن به این هدف، از کدی مانند زیر استفاده میکنم:
{opened && <SomeElement />}
این باعث میشود که SomeElement تنها در صورتی نمایش داده شود که opened درست باشد. این به دلیل نحوه حل شرایط منطقی در جاوااسکریپت کار میکند:
true && true && 2; // will output 2
true && false && 2; // will output false
true && 'some string'; // will output 'some string'
opened && <SomeElement />; // will output SomeElement if `opened` is true, will output false otherwise (and false will be ignored by react during rendering)
// be careful with 'falsy' values eg
const someValue = [];
someValue.length && <SomeElement /> // will output 0, which will be rednered by react
// it'll be better to:
someValue.length > 0 && <SomeElement /> // will render nothing as we cast the value to boolean
دلایل استفاده از این روش به جای CSS 'display: none';
در حالی که ممکن است «ارزانتر» باشد که یک عنصر را با CSS مخفی کنید - در چنین حالتی عنصر «مخفی» هنوز در دنیای React «زنده» است (که ممکن است در واقع آن را بسیار گرانتر کند)
- این بدان معناست که اگر props عنصر والد (مثلاً
<TabView>) تغییر کند - حتی اگر فقط یک تب را ببینید، همه ۵ تب دوباره رندر خواهند شد
- این بدان معناست که اگر props عنصر والد (مثلاً
- عنصر مخفی ممکن است هنوز برخی از متدهای چرخه حیات را اجرا کند - مثلا، ممکن است پس از هر بهروزرسانی دادهای از سرور دریافت کند حتی اگر قابل مشاهده نباشد
- عنصر مخفی ممکن است برنامه را خراب کند اگر دادههای نادرست دریافت کند. این ممکن است اتفاق بیفتد چون ممکن است هنگام بهروزرسانی حالت، از گرههای نامرئی «غفلت» کنید.
- شما ممکن است به اشتباه استایل 'display' نادرست را هنگام قابل مشاهده کردن عنصر تنظیم کنید - به عنوان مثال، بعضی div به صورت پیشفرض 'display: flex' است، اما شما به اشتباه 'display: block' را با
display: invisible ? 'block' : 'none'تنظیم میکنید که ممکن است چیدمان را خراب کند - استفاده از
someBoolean && <SomeNode />بسیار ساده برای فهمیدن و استدلال کردن است، به خصوص اگر منطق شما مربوط به نمایش چیزی یا ندادن آن پیچیده شود - در بسیاری از موارد، شما میخواهید وضعیت عنصر را زمانی که دوباره ظاهر میشود، «بازنشانی» کنید. به عنوان مثال، ممکن است یک اسلایدر داشته باشید که بخواهید هر بار که نمایش داده میشود به موقعیت اولیه خود برگردد. (اگر رفتار مورد نظر این است که وضعیت قبلی عنصر حفظ شود، حتی اگر مخفی باشد، که به نظر من نادر است - در این صورت من واقعاً استفاده از CSS را در نظر میگیرم اگر یادآوری این وضعیت به روش دیگری پیچیده باشد)
پاسخ شماره ۵
با جدیدترین نسخه ریاکت ۰.۱۱، شما همچنین میتوانید فقط null برگردانید تا هیچ محتوایی رندر نشود.
رندر به تهی
پاسخ شماره ۶
این یک روش خوب برای استفاده از DOM مجازی است:
class Toggle extends React.Component {
state = {
show: true,
}
toggle = () => this.setState((currentState) => ({show: !currentState.show}));
render() {
return (
<div>
<button onClick={this.toggle}>
toggle: {this.state.show ? 'show' : 'hide'}
</button>
{this.state.show && <div>Hi there</div>}
</div>
);
}
}
مثال اینجا
استفاده از هوکهای React:
const Toggle = () => {
const [show, toggleShow] = React.useState(true);
return (
<div>
<button
onClick={() => toggleShow(!show)}
>
toggle: {show ? 'show' : 'hide'}
</button>
{show && <div>Hi there</div>}
</div>
)
}
مثال اینجا
پاسخ شماره ۷
من یک کامپوننت کوچک ایجاد کردم که این کار را برای شما انجام میدهد: react-toggle-display
این ویژگی style را بر اساس props های hide یا show به display: none !important تنظیم میکند.
نمونه استفاده:
var ToggleDisplay = require('react-toggle-display');
var Search = React.createClass({
getInitialState: function() {
return { showResults: false };
},
onClick: function() {
this.setState({ showResults: true });
},
render: function() {
return (
<div>
<input type="submit" value="Search" onClick={this.onClick} />
<ToggleDisplay show={this.state.showResults}>
<Results />
</ToggleDisplay>
</div>
);
}
});
var Results = React.createClass({
render: function() {
return (
<div id="results" className="search-results">
Some Results
</div>
);
}
});
React.renderComponent(<Search />, document.body);
پاسخ شماره ۸
قبلاً چندین پاسخ عالی وجود دارد، اما فکر نمیکنم خیلی خوب توضیح داده شده باشند و چندین روش ارائه شده شامل نکات ریزی است که ممکن است افراد را گیج کند. بنابراین من میخواهم سه روش اصلی (به علاوه یک گزینه خارج از موضوع) برای انجام این کار را مرور کنم و مزایا و معایبشان را توضیح دهم. عمدتاً دارم این را مینویسم زیرا گزینه اول خیلی توصیه شده و اگر به درستی استفاده نشود، با مشکلات زیادی همراه است.
گزینه ۱: رندر مشروط در والد.
من این روش را دوست ندارم مگر اینکه فقط بخواهید کامپوننت را یک بار رندر کنید و همانجا رها کنید. مشکل این است که باعث میشود React هر بار که دید را تغییر میدهید، کامپوننت را از نو بسازد. در اینجا یک مثال است. LogoutButton یا LoginButton به طور شرطی در کامپوننت والد LoginControl رندر میشوند. اگر این را اجرا کنید، متوجه خواهید شد که constructor با هر کلیک روی دکمه فراخوانی میشود. https://codepen.io/Kelnor/pen/LzPdpN?editors=1111
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button = null;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
}
class LogoutButton extends React.Component{
constructor(props, context){
super(props, context)
console.log('created logout button');
}
render(){
return (
<button onClick={this.props.onClick}>
Logout
</button>
);
}
}
class LoginButton extends React.Component{
constructor(props, context){
super(props, context)
console.log('created login button');
}
render(){
return (
<button onClick={this.props.onClick}>
Login
</button>
);
}
}
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);
اکنون React در ایجاد کامپوننتها از ابتدا نسبتاً سریع است. با این حال، هنوز باید هنگام ایجاد، کد شما را فراخوانی کند. بنابراین اگر کد سازنده (constructor)، componentDidMount، render و غیره شما پرهزینه باشد، به طور قابل توجهی نمایش کامپوننت را کند میکند. این همچنین به این معناست که نمیتوانید از این روش با کامپوننتهای دارای state استفاده کنید که میخواهید وضعیت آنها هنگام مخفی شدن حفظ شود (و هنگام نمایش دوباره بازیابی شود). یکی از مزایا این است که کامپوننت مخفی اصلاً تا وقتی انتخاب نشده ایجاد نمیشود. بنابراین کامپوننتهای مخفی بارگذاری اولیه صفحه شما را به تأخیر نمیاندازند. همچنین ممکن است مواردی وجود داشته باشد که بخواهید یک کامپوننت دارای state هنگام جابجایی ریست شود. در چنین حالتی این بهترین گزینه شما است.
گزینه ۲: رندر مشروط در فرزند
این هر دو جزء را یک بار ایجاد میکند. سپس باقی کد رندر را در صورتی که جزء مخفی باشد، دور میزند. شما همچنین میتوانید منطق دیگر را در متدهای دیگر با استفاده از ویژگی visible دور بزنید. به console.log در صفحه codepen توجه کنید. https://codepen.io/Kelnor/pen/YrKaWZ?editors=0011
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
<LoginButton isLoggedIn={isLoggedIn} onClick={this.handleLoginClick}/>
<LogoutButton isLoggedIn={isLoggedIn} onClick={this.handleLogoutClick}/>
</div>
);
}
}
class LogoutButton extends React.Component{
constructor(props, context){
super(props, context)
console.log('created logout button');
}
render(){
if(!this.props.isLoggedIn){
return null;
}
return (
<button onClick={this.props.onClick}>
Logout
</button>
);
}
}
class LoginButton extends React.Component{
constructor(props, context){
super(props, context)
console.log('created login button');
}
render(){
if(this.props.isLoggedIn){
return null;
}
return (
<button onClick={this.props.onClick}>
Login
</button>
);
}
}
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);
حالا، اگر منطق مقداردهی اولیه سریع باشد و فرزندان بدون حالت باشند، پس تفاوتی در عملکرد یا کارکرد مشاهده نخواهید کرد. با این حال، چرا اصلاً میخواهیم React هر بار که تغییر وضعیت میدهد یک کامپوننت جدید بسازد؟ اگر مقداردهی اولیه گران باشد، گزینه ۱ هر بار که یک کامپوننت تغییر وضعیت میدهد آن را اجرا میکند که باعث کند شدن صفحه هنگام تعویض میشود. گزینه ۲ همه مقداردهیهای کامپوننت را در بارگذاری اول صفحه اجرا میکند و باعث کند شدن بارگذاری اولیه میشود. دوباره باید ذکر کرد، اگر فقط یک بار کامپوننت را بر اساس یک شرط نشان میدهید و آن را تغییر وضعیت نمیدهید، یا میخواهید هنگام تغییر وضعیت بازنشانی شود، گزینه ۱ مناسب است و احتمالاً بهترین گزینه است.
با این حال، اگر بارگذاری کند صفحه یک مشکل است، به این معناست که کد پرهزینهای در یکی از متدهای چرخه حیات دارید و این معمولاً ایده خوبی نیست. شما میتوانید، و احتمالاً باید، مشکل بارگذاری کند صفحه را با خارج کردن کد پرهزینه از متدهای چرخه حیات حل کنید. آن را به یک تابع ناهمزمان منتقل کنید که توسط ComponentDidMount فراخوانی میشود و از تابع callback استفاده کنید تا آن را در یک متغیر حالت با setState() قرار دهید. اگر متغیر حالت null باشد و کامپوننت قابل مشاهده باشد، تابع render یک جایگزین (placeholder) بازگرداند. در غیر این صورت، دادهها را render کنید. به این ترتیب، صفحه سریع بارگذاری میشود و تبها به محض بارگذاری پر میشوند. شما همچنین میتوانید منطق را به والد منتقل کرده و نتایج را به عنوان props به فرزندان ارائه دهید. به این ترتیب میتوانید اولویتبندی کنید که کدام تبها ابتدا بارگذاری شوند. یا نتایج را کش کنید و منطق را فقط دفعه اولی که یک کامپوننت نمایش داده میشود اجرا کنید.
گزینه ۳: مخفی کردن کلاس
مخفی کردن کلاس احتمالاً آسانترین روش برای اجرا است. همانطور که گفته شد، شما فقط یک کلاس CSS با display: none ایجاد میکنید و کلاس را بر اساس prop اختصاص میدهید. عیب این روش این است که کل کد هر کامپوننت مخفی فراخوانی میشود و همه کامپوننتهای مخفی به DOM متصل میشوند. (گزینه ۱ اصلاً کامپوننتهای مخفی را ایجاد نمیکند و گزینه ۲ وقتی کامپوننت مخفی است، کد غیرضروری را کوتاه میکند و کامپوننت را بهطور کامل از DOM حذف میکند.) به نظر میرسد این روش در تغییر حالت دیدن سریعتر است، طبق برخی تستهایی که توسط سایر کاربران در پاسخهای دیگر انجام شده، اما من نمیتوانم درباره آن قطعیت داشته باشم.
گزینه ۴: یک کامپوننت ولی تغییر Props. یا شاید اصلاً هیچ کامپوننتی نباشد و HTML را کش کنیم.
این مورد برای همه برنامهها کار نخواهد کرد و خارج از موضوع است زیرا مربوط به پنهان کردن اجزا نیست، اما ممکن است برای برخی موارد استفاده راه حل بهتری نسبت به پنهان کردن باشد. فرض کنید شما تب دارید. ممکن است بتوان یک کامپوننت React نوشت و فقط با استفاده از props مشخص کرد که چه چیزی در تب نمایش داده شود. همچنین میتوانید JSX را در متغیرهای state ذخیره کنید و از یک prop برای تعیین اینکه کدام JSX در تابع render برگردانده شود استفاده کنید. اگر لازم است JSX تولید شود، آن را تولید کرده و در کامپوننت والد کش کنید و نسخه صحیح را به عنوان prop ارسال کنید. یا اینکه در فرزند تولید شده و در state فرزند کش شود و از props برای انتخاب نسخه فعال استفاده کنید.
پاسخ شماره ۹
شما یک مقدار بولی در حالت (مثلاً 'show') تنظیم میکنید، و سپس انجام میدهید:
var style = {};
if (!this.state.show) {
style.display = 'none'
}
return <div style={style}>...</div>
