Условный рендеринг¶
React позволяет разделить логику на независимые компоненты. Эти компоненты можно показывать или прятать в зависимости от текущего состояния.
Условный рендеринг в React работает так же, как условные выражения работают в JavaScript. Бывает нужно объяснить React, как состояние влияет на то, какие компоненты требуется скрыть, а какие — отрендерить, и как именно. В таких ситуациях используйте условный оператор JavaScript или выражения подобные if
.
Рассмотрим два компонента:
function UserGreeting(props) {
return <h1>С возвращением!</h1>
}
function GuestGreeting(props) {
return <h1>Войдите, пожалуйста.</h1>
}
Можно создать компонент Greeting
, который отражает один из этих компонентов в зависимости от того, на сайте пользователь или нет:
function Greeting(props) {
const isLoggedIn = props.isLoggedIn
if (isLoggedIn) {
return <UserGreeting />
}
return <GuestGreeting />
}
ReactDOM.render(
// Попробуйте заменить на isLoggedIn={true} и посмотрите на эффект.
<Greeting isLoggedIn={false} />,
document.getElementById('root')
)
В этом примере рендерится различное приветствие в зависимости от значения пропа isLoggedIn
.
Переменные-элементы¶
Элементы React можно сохранять в переменных. Это может быть удобно, когда какое-то условие определяет, надо ли рендерить одну часть компонента или нет, а другая часть компонента остаётся неизменной.
Рассмотрим ещё два компонента, представляющих кнопки Войти (Login) и Выйти (Logout).
function LoginButton(props) {
return <button onClick={props.onClick}>Войти</button>
}
function LogoutButton(props) {
return <button onClick={props.onClick}>Выйти</button>
}
В следующем примере мы создадим компонент с состоянием и назовём его LoginControl
.
Он будет рендерить либо <LoginButton />
, либо <LogoutButton />
в зависимости от текущего состояния. А ещё он будет всегда рендерить <Greeting />
из предыдущего примера.
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
if (isLoggedIn) {
button = (
<LogoutButton onClick={this.handleLogoutClick} />
)
} else {
button = (
<LoginButton onClick={this.handleLoginClick} />
)
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
)
}
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
)
Нет ничего плохого в том, чтобы объявить переменную и условно рендерить компонент if
-выражением. Но иногда хочется синтаксис покороче. Давайте посмотрим на несколько других способов писать условия прямо в JSX.
Встроенные условия if с логическим оператором &&¶
Вы можете внедрить любое выражение в JSX, заключив его в фигурные скобки. Это правило распространяется и на логический оператор &&
языка JavaScript, которым можно удобно вставить элемент в зависимости от условия:
function Mailbox(props) {
const unreadMessages = props.unreadMessages
return (
<div>
<h1>Здравствуйте!</h1>
{unreadMessages.length > 0 && (
<h2>
У вас {unreadMessages.length} непрочитанных
сообщений.
</h2>
)}
</div>
)
}
const messages = ['React', 'Re: React', 'Re:Re: React']
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById('root')
)
Приведённый выше вариант работает корректно, потому что в JavaScript выражение true && expression
всегда вычисляется как expression
, а выражение false && expression
— как false
.
То есть, если условие истинно (true
), то элемент, идущий непосредственно за &&
, будет передан на вывод. Если же оно ложно (false
), то React проигнорирует и пропустит его.
Встроенные условия if-else с тернарным оператором¶
Есть ещё один способ писать условия прямо в JSX. Вы можете воспользоваться тернарным оператором condition ? true : false
.
Вот как этот метод можно использовать, чтобы отрендерить кусочек текста.
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
Пользователь <b>{isLoggedIn ? 'сейчас' : 'не'}</b> на сайте.
</div>
);
}
Этот метод можно использовать и с выражениями покрупнее, но это может сделать код менее очевидным:
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn ? (
<LogoutButton onClick={this.handleLogoutClick} />
) : (
<LoginButton onClick={this.handleLoginClick} />
)}
</div>
);
}
Как в JavaScript, так и в React выбор синтаксиса зависит от ваших предпочтений и принятого в команде стиля. Не забывайте, что если какое-то условие выглядит очень сложным, возможно пришло время извлечь часть кода в отдельный компонент.
Предотвращение рендеринга компонента¶
В редких случаях может потребоваться позволить компоненту спрятать себя, хотя он уже и отрендерен другим компонентом. Чтобы этого добиться, верните null
вместо того, что обычно возвращается на рендеринг.
Например, будет ли содержимое <WarningBanner />
отрендерено, зависит от значения пропа под именем warn
. Если значение false
, компонент ничего не рендерит:
function WarningBanner(props) {
if (!props.warn) {
return null
}
return <div className="warning">Предупреждение!</div>
}
class Page extends React.Component {
constructor(props) {
super(props)
this.state = { showWarning: true }
this.handleToggleClick = this.handleToggleClick.bind(
this
)
}
handleToggleClick() {
this.setState((state) => ({
showWarning: !state.showWarning,
}))
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} />
<button onClick={this.handleToggleClick}>
{this.state.showWarning ? 'Спрятать' : 'Показать'}
</button>
</div>
)
}
}
ReactDOM.render(<Page />, document.getElementById('root'))
Сам факт возврата null
из метода render
компонента никак не влияет на срабатывание методов жизненного цикла компонента. Например, componentDidUpdate
будет всё равно вызван.