Показать или скрыть элемент в React
Я впервые работаю с 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);
Как мне сделать так, чтобы при клике отображался div с результатами?
5 ответ(ов)
Чтобы скрыть элемент в React в зависимости от переданного свойства shouldHide
, вы можете использовать условное присваивание класса. Вот пример, как это можно сделать:
<style type="text/css">
.hidden { display: none; }
</style>
const Example = props =>
<div className={props.shouldHide ? 'hidden' : undefined}>Hello</div>;
В этом коде, если props.shouldHide
истинно (или true
), элемент div
получит класс hidden
, который скрывает его благодаря определению в CSS (display: none;
). Если же shouldHide
ложно, класс не будет применяться, и элемент будет видимым.
Вот мой подход.
import React, { useState } from 'react';
function ToggleBox({ title, children }) {
const [isOpened, setIsOpened] = useState(false);
function toggle() {
setIsOpened(prevState => !prevState);
}
return (
<div className="box">
<div className="boxTitle" onClick={toggle}>
{title}
</div>
{isOpened && (
<div className="boxContent">
{children}
</div>
)}
</div>
);
}
В приведенном коде я использую следующий подход для достижения этой цели:
{opened && <SomeElement />}
Это будет рендерить SomeElement
только в том случае, если opened
истинно. Это работает благодаря тому, как JavaScript обрабатывает логические условия:
true && true && 2; // выведет 2
true && false && 2; // выведет false
true && 'some string'; // выведет 'some string'
opened && <SomeElement />; // выведет SomeElement, если `opened` истинно; иначе выведет false (и false будет проигнорировано React при рендеринге)
// будьте осторожны с 'ложными' значениями, например:
const someValue = [];
someValue.length && <SomeElement />; // выведет 0, который будет отрендерен React
// лучше использовать:
someValue.length > 0 && <SomeElement />; // не будет рендерить ничего, поскольку мы преобразуем значение в логическое
Причины использовать этот подход вместо CSS 'display: none':
- Хотя может показаться, что скрыть элемент с помощью CSS дешевле, в таком случае скрытый элемент все еще "жив" в React (что на самом деле может оказаться гораздо более затратным).
- Это означает, что если свойства родительского элемента (например,
<TabView>
) изменятся, даже если вы видите только одну вкладку, все 5 вкладок будут перерендерены. - Скрытый элемент все еще может выполнять некоторые методы жизненного цикла — например, он может запрашивать данные с сервера после каждого обновления, даже если он не виден.
- Скрытый элемент может привести к сбою приложения, если он получит неправильные данные. Это может произойти, поскольку вы можете "забыть" о невидимых узлах при обновлении состояния.
- Вы можете по ошибке установить неправильный стиль 'display', делая элемент видимым — например, какой-то блок по умолчанию имеет 'display: flex', но вы по ошибке установите 'display: block' с помощью
display: invisible ? 'block' : 'none'
, что может сломать раскладку. - Использование
someBoolean && <SomeNode />
очень простое для понимания и осмысления, особенно если ваша логика, связанная с отображением чего-то или нет, становится сложной. - Во многих случаях вы хотите "обновить" состояние элемента, когда он снова появляется. Например, у вас может быть слайдер, который вы хотите установить в начальное положение каждый раз, когда он показывается. (Если это желаемое поведение — сохранить состояние предыдущего элемента, даже если он скрыт, что, на мой взгляд, бывает редко, я бы действительно подумал о использовании CSS, если бы запоминание этого состояния иным способом было бы сложным.)
- Это означает, что если свойства родительского элемента (например,
Уже есть несколько отличных ответов на этот вопрос, но мне кажется, что они не совсем хорошо объясняют некоторые нюансы, и многие из предоставленных методов могут содержать подводные камни, которые могут запутать людей. Поэтому я собираюсь рассмотреть три основных метода (плюс один не совсем по теме) и объяснить их плюсы и минусы. Я пишу это в основном потому, что Опция 1 была рекомендована довольно часто, и у этой опции есть много потенциальных проблем, если ее неправильно использовать.
Опция 1: Условный рендеринг в родительском компоненте
Мне не нравится этот метод, если вы собираетесь рендерить компонент только один раз и оставить его. Проблема заключается в том, что это заставит React создавать компонент заново каждый раз, когда вы переключаете видимость. Вот пример. Кнопки выхода (LogoutButton) или входа (LoginButton) условно рендерятся в родительском компоненте LoginControl. Если вы запустите это, вы заметите, что конструктор вызывается при каждом клике по кнопке. Ссылка на пример
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('создан компонент кнопки выхода');
}
render(){
return (
<button onClick={this.props.onClick}>
Выйти
</button>
);
}
}
class LoginButton extends React.Component{
constructor(props, context){
super(props, context)
console.log('создан компонент кнопки входа');
}
render(){
return (
<button onClick={this.props.onClick}>
Войти
</button>
);
}
}
function UserGreeting(props) {
return <h1>С возвращением!</h1>;
}
function GuestGreeting(props) {
return <h1>Пожалуйста, зарегистрируйтесь.</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);
Сейчас React довольно быстро создает компоненты с нуля. Однако ему все равно нужно вызывать ваш код при создании. Поэтому, если ваш код в конструкторе, компоненте componentDidMount
, рендере и т. д. является затратным, это значительно замедлит отображение компонента. Также это означает, что вы не можете использовать это с компонентами, содержащими состояние, если хотите, чтобы состояние сохранялось при скрытии (и восстанавливалось при отображении). Единственное преимущество заключается в том, что скрытый компонент вообще не создается до тех пор, пока он не будет выбран. Таким образом, скрытые компоненты не задерживают первоначальную загрузку страницы. Также могут быть случаи, когда вы хотите, чтобы состояние компонента сбрасывалось при переключении. В этом случае это ваш лучший вариант.
Опция 2: Условный рендеринг в дочерних компонентах
Этот метод создает оба компонента сразу. Затем он «обходит» оставшийся код рендеринга, если компонент скрыт. Вы также можете «обойти» другую логику в других методах, используя видимый пропс. Обратите внимание на console.log
на странице codepen. Ссылка на пример
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('создан компонент кнопки выхода');
}
render(){
if(!this.props.isLoggedIn){
return null;
}
return (
<button onClick={this.props.onClick}>
Выйти
</button>
);
}
}
class LoginButton extends React.Component{
constructor(props, context){
super(props, context)
console.log('создан компонент кнопки входа');
}
render(){
if(this.props.isLoggedIn){
return null;
}
return (
<button onClick={this.props.onClick}>
Войти
</button>
);
}
}
function UserGreeting(props) {
return <h1>С возвращением!</h1>;
}
function GuestGreeting(props) {
return <h1>Пожалуйста, зарегистрируйтесь.</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);
Теперь, если логика инициализации быстрая, а дочерние компоненты не имеют состояния, вы не заметите разницы в производительности или функциональности. Однако зачем заставлять React создавать совершенно новый компонент при каждом переключении? Если инициализация дорогая, то Опция 1 будет запускать ее каждый раз при переключении компонента, что замедлит страницу. Опция 2 запустит всю инициализацию компонентов при первой загрузке страницы. Необходимо отметить, что если вы просто показываете компонент один раз на основе условия и не переключаете его, или хотите, чтобы он сбрасывался при переключении, тогда Опция 1 вполне подходит и, вероятно, является лучшим вариантом.
Если медленная загрузка страницы является проблемой, это означает, что у вас есть дорогостоящий код в жизненном цикле компонента, и, как правило, это не является хорошей практикой. Вы можете и, вероятно, должны решить проблему медленной загрузки страницы, переместив дорогой код из методов жизненного цикла. Переместите его в асинхронную функцию, которая будет запущена в componentDidMount
, а колбек должен сохранять результат в переменной состояния с помощью setState()
. Если переменная состояния равна null
, а компонент видим, то функция рендеринга должна возвращать заполнители. В противном случае рендерьте данные. Таким образом, страница загрузится быстро и будет заполнять вкладки по мере их загрузки. Вы также можете перенести логику в родительский компонент и передавать результаты дочерним компонентам в виде пропсов. Таким образом, вы сможете приоритизировать загрузку каких вкладок будет происходить в первую очередь или кешировать результаты и запускать логику только при первом показе компонента.
Опция 3: Скрытие классов (Class Hiding)
Скрытие классов, вероятно, самый простой способ реализации. Как упоминалось ранее, вы просто создаете CSS класс с display: none
и назначаете этот класс на основе пропса. Недостаток заключается в том, что весь код каждого скрытого компонента вызывается, и все скрытые компоненты привязаны к DOM. (Опция 1 вообще не создает скрытые компоненты, а Опция 2 обходила ненужный код, когда компонент скрыт и полностью удаляла компонент из DOM.) Похоже, что это быстрейший способ переключения видимости согласно некоторым тестам, проведенным комментаторами на других ответах, но я не могу это подтвердить.
Опция 4: Один компонент, но изменение пропсов
Или, возможно, вообще без компонента и кеширование HTML.
Этот вариант не подойдет для каждого приложения, и он не по теме, потому что не касается скрытия компонентов, но для некоторых случаев использования это может быть лучшее решение, чем скрытие. Допустим, у вас есть вкладки. Возможно, можно написать один React компонент и просто использовать пропсы для изменения того, что отображается на вкладке. Вы также можете сохранить JSX в переменные состояния и использовать пропсы, чтобы решить, какой JSX вернуть в функции рендеринга. Если JSX нужно сгенерировать, то сделайте это и закешируйте в родительском компоненте, передав правильный вариант как пропс. Или создайте в дочернем компоненте и закешируйте его в состоянии дочернего компонента, используя пропсы для выбора активного.
Вы устанавливаете булевое значение в состоянии (например, 'show'), и затем используете следующий код:
var style = {};
if (!this.state.show) {
style.display = 'none';
}
return <div style={style}>...</div>;
В этом примере, если this.state.show
имеет значение false
, стиль блока <div>
будет установлен как display: 'none'
, что приведет к скрытию элемента на странице. Если this.state.show
будет true
, никаких дополнительных стилей не будет применено, и элемент будет отображаться по умолчанию. Убедитесь, что вы правильно обновляете состояние show
, чтобы контролировать видимость вашего компонента.
Для того чтобы создать простой метод показа/скрытия элементов в React с использованием Hooks, вы можете сделать следующее:
- Определите состояние с помощью
useState
, чтобы отслеживать, будет ли текст отображаться. Создайте переменную состоянияshowText
и функцию для её обновленияsetShowText
:
const [showText, setShowText] = useState(false);
- Теперь добавьте логику в ваш метод рендеринга, чтобы отображать элемент в зависимости от значения
showText
:
{showText && <div>Этот текст будет показан!</div>}
- И наконец, добавьте обработчик события
onClick
, который будет переключать значениеshowText
:
onClick={() => setShowText(!showText)}
Вот и всё! Теперь при клике текст будет появляться и исчезать. Отличная работа!
Как программно выполнять навигацию с помощью React Router?
Как условно добавлять атрибуты к компонентам React?
Как установить фокус на поле ввода после рендеринга?
Метод set в useState не отражает изменения немедленно
В чем разница между React Native и React?