Сегодня, в переводе следующей части учебного курса по React, мы поговорим об условном рендеринге.
→ Часть 1: обзор курса, причины популярности React, ReactDOM и JSX
→ Часть 2: функциональные компоненты
→ Часть 3: файлы компонентов, структура проектов
→ Часть 4: родительские и дочерние компоненты
→ Часть 5: начало работы над TODO-приложением, основы стилизации
→ Часть 6: о некоторых особенностях курса, JSX и JavaScript
→ Часть 7: встроенные стили
→ Часть 8: продолжение работы над TODO-приложением, знакомство со свойствами компонентов
→ Часть 9: свойства компонентов
→ Часть 10: практикум по работе со свойствами компонентов и стилизации
→ Часть 11: динамическое формирование разметки и метод массивов map
→ Часть 12: практикум, третий этап работы над TODO-приложением
→ Часть 13: компоненты, основанные на классах
→ Часть 14: практикум по компонентам, основанным на классах, состояние компонентов
→ Часть 15: практикумы по работе с состоянием компонентов
→ Часть 16: четвёртый этап работы над TODO-приложением, обработка событий
→ Часть 17: пятый этап работы над TODO-приложением, модификация состояния компонентов
→ Часть 18: шестой этап работы над TODO-приложением
→ Часть 19: методы жизненного цикла компонентов
→ Часть 20: первое занятие по условному рендерингу
Занятие 36. Условный рендеринг, часть 1
→ Оригинал
Технологии условного рендеринга используются в тех случаях, когда что-то нужно вывести на страницу в соответствии с неким условием. На этом занятии мы поговорим о том, как вывести особое сообщение (оно вполне может быть представлено чем-то вроде загрузочного экрана) в то время, когда приложение готовится к работе, загружая данные, и, после того, как оно будет готово, как заменить это сообщение на что-то другое.
Экспериментировать сегодня мы будем с приложением, созданным средствами create-react-app, в файле App.js
которого содержится следующий код:
import React, {Component} from "react"
import Conditional from "./Conditional"
class App extends Component {
constructor() {
super()
this.state = {
isLoading: true
}
}
componentDidMount() {
setTimeout(() => {
this.setState({
isLoading: false
})
}, 1500)
}
render() {
return (
<div>
<Conditional isLoading={this.state.isLoading}/>
</div>
)
}
}
export default App
Кроме того, в той же папке, где находится файл App.js
, есть файл компонента Conditional.js
со следующим содержимым:
import React from "react"
function Conditional(props) {
return (
)
}
export default Conditional
На данном этапе работы это приложение пока работать не будет, в процессе разбора материала мы это исправим.
Одна из сложностей, встающих перед теми, кто осваивает React, заключается в том, что им приходится изучать множество инструментов, которые можно использовать различными способами. Программист не обязательно ограничен лишь одним способом использования некоего средства. Определённое влияние на это оказывает тот факт, что React-разработка чрезвычайно близка к разработке на обычном JavaScript.
Поэтому у нас есть возможности использования разных подходов к решению одинаковых задач, поэтому одни и те же инструменты могут использоваться по-разному. Условный рендеринг — это та область React, в которой вышеозвученные идеи проявляют себя особенно сильно. Собственно говоря, прежде чем мы начнём, хотелось бы отметить, что, хотя мы разберём несколько подходов к применению этой технологии, ими реальные варианты её использования не ограничиваются.
Поговорим о коде, с которым мы сейчас будем экспериментировать. У нас, в файле App.js
, имеется компонент, основанный на классе. В его конструкторе инициализировано состояние, содержащее свойство isLoading
, установленное в значение true
. Подобная конструкция часто применяется в случаях, когда для приведения компонента в рабочее состояние нужно, например, выполнять запросы к некоему API, и, пока компонент ожидает поступления данных и разбирает их, нужно что-то показать на экране. Возможно, на выполнение обращения к API требуется 3-4 секунды, и вы не хотите, чтобы пользователь, глядя на экран, думал бы, что ваше приложение дало сбой. В результате в состоянии есть свойство, которое указывает на то, выполняет ли в настоящий момент приложение некие служебные действия. И условный рендеринг будет использоваться для вывода на экран чего-то, что сообщает пользователю о том, что приложение в настоящий момент что-то загружает в фоне.
В коде компонента App
есть метод componentDidMount()
, который мы уже совсем скоро обсудим. Пока же обратим внимание на метод render()
. Здесь мы выводим компонент Condition
, который импортирован в коде, находящемся в верхней части файла App.js
. Этому компоненту передаётся свойство isLoading
, представляющее собой текущее значение свойства isLoading
из состояния компонента App
. Код компонента Conditional
пока не возвращает ничего такого, что можно вывести на экран, этим компонентом мы займёмся немного позже. Пока же давайте вернёмся к методу componentDidMount()
из кода компонента App
.
Вспомните о том, что метод componentDidMount()
даёт нам возможность выполнять некий код сразу после того, как компонент, в нашем случае это компонент App
, впервые будет выведен на экран. В коде этого метода мы имитируем обращение к некоему API. Здесь мы устанавливаем таймер на полторы секунды. Когда это время пройдёт — будет запущен код функции, переданной функции setTimeout()
. В этой функции, исходя из предположения о том, что её вызов символизирует окончание загрузки данных из API, выполняется изменение состояния. А именно, его свойство isLoading
устанавливается в значение false
. Это говорит о том, что загрузка данных завершена, и приложение после этого может нормально работать. На будущих занятиях мы поговорим об использовании функции fetch()
для загрузки данных, пока же ограничимся вышеописанной имитацией этого процесса.
Кстати, тут будет уместно ещё раз поднять тему методов жизненного цикла компонента. Дело в том, что как только свойство состояния isLoading
меняется с true
на false
, компонент Conditional
получает новое значение свойства. Сначала, при первом выводе компонента на экран, он получает, в свойстве isLoading
, значение true
, а затем, после того, как состояние меняется, он получает то же свойство с новым значением. Собственно говоря, при изменении состояния повторно вызывается метод render()
, в результате компонент Conditional
также будет повторно выведен на экран. Напомним о том, что Conditional
— это обычный функциональный компонент, то есть его повторный рендеринг означает повторный вызов функции, которой он представлен. Но то, что мы возвращаем из этой функции при повторном рендеринге компонента, может отличаться от того, что возвращалось ранее. Причиной такого изменения является изменение того, что мы передаём компоненту.
Итак, компонент Conditional
принимает свойство isLoading
. Прежде чем мы приступим к работе над кодом, проверим, работают ли те механизмы, которые в нём уже имеются. Для этого мы вернём из компонента некую разметку и выведем в консоль значение props.isLoading
. После этого код компонента будет выглядеть так:
import React from "react"
function Conditional(props) {
console.log(props.isLoading)
return (
<h1>Temp</h1>
)
}
export default Conditional
Страница приложения после этого будет выглядеть так, как показано на следующем рисунке.
Страница приложения в браузере
Обратите внимание на то, что true
выводится в консоль сразу же после загрузки приложения, а false
— через 1.5 секунды. Это происходит благодаря работе вышеописанного механизма в методе componentDidMount()
компонента App
.
Теперь поговорим об условном рендеринге. Его суть заключается в том, что мы выводим что-то на экран только в том случае, если выполняется некое условие. В данном случае, вместо вывода на страницу строки Temp
, мы, в компоненте Conditional
, можем проверить значение props.isLoading
, и, если оно равно true
, вывести на страницу текст Loading...
. Если же это значение равно false
, что символизирует окончание загрузки, из компонента можно вернуть какой-нибудь другой текст. В коде это будет выглядеть так:
import React from "react"
function Conditional(props) {
if(props.isLoading === true) {
return (
<h1>Loading...</h1>
)
} else {
return (
<h1>Some cool stuff about conditional rendering</h1>
)
}
}
export default Conditional
Попробуйте запустить у себя этот код, обновите страницу и понаблюдайте за тем, как, при загрузке страницы, выводится один текст, а через некоторое время — другой.
Учитывая особенности JavaScript, мы можем упростить вышеприведённый код так:
import React from "react"
function Conditional(props) {
if(props.isLoading === true) {
return (
<h1>Loading...</h1>
)
}
return (
<h1>Some cool stuff about conditional rendering</h1>
)
}
export default Conditional
Если условие, проверяемое в блоке if
, является истинным, то сработает выражение return
, находящееся в этом блоке, после чего выполнение функции завершится. Если же условие является ложным, то выражение return
из этого блока не выполняется и осуществляется возврат из функции того, что задано во втором выражении return
.
Сейчас давайте поговорим о том, как можно решать задачи условного рендеринга с использованием тернарного оператора. Эта конструкция существует в JavaScript уже очень давно. Её часто используют в React для решения задач условного рендеринга. Вот как она выглядит:
условие ? выражение1 : выражение2
Значение выражения 1 возвращается в том случае, если условие истинно, значение выражения 2 — в том случае, если условие ложно.
В нашем случае с использованием тернарного оператора код компонента Conditional
можно переписать так:
import React from "react"
function Conditional(props) {
return (
props.isLoading === true ? <h1>Loading...</h1> : <h1>Some cool stuff about conditional rendering</h1>
)
}
export default Conditional
Такая конструкция, хотя и работает, выглядит непривычно. Дело в том, что обычно компоненты возвращают более сложные конструкции. Поэтому обернём всё это в элемент <div>
:
import React from "react"
function Conditional(props) {
return (
<div>
props.isLoading === true ? <h1>Loadinаg...</h1> : <h1>вSome cool stuff about conditional rendering</h1>
</div>
)
}
export default Conditional
Такой код тоже работает, правда уже не так, как нужно. На страницу попадает всё то, что заключено в элемент <div>l;
. Для того, чтобы это исправить, вспомним о том, что JS-конструкции, используемые в разметке, возвращаемой из компонентов, нужно заключать в фигурные скобки и соответствующим образом перепишем код:
import React from "react"
function Conditional(props) {
return (
<div>
{props.isLoading === true ? <h1>Loading...</h1> : <h1>Some cool stuff about conditional rendering</h1>}
</div>
)
}
export default Conditional
Теперь всё снова работает так, как надо.
Надо отметить, что в реальном компоненте разметка, возвращаемая им, выглядела бы сложнее. Тут, например, в верхней части того, что выводит компонент, может присутствовать некая навигационная панель, в нижней части может быть предусмотрен «подвал» страницы, и так далее. Выглядеть это может так:
import React from "react"
function Conditional(props) {
return (
<div>
<h1>Navbar</h1>
{props.isLoading === true ? <h1>Loading...</h1> : <h1>Some cool stuff about conditional rendering</h1>}
<h1>Footer</h1>
</div>
)
}
export default Conditional
При этом наличие в разметке, возвращаемой компонентом, дополнительных элементов, не мешает механизмам условного рендеринга. Кроме того, эти элементы будут выводиться и тогда, когда props.isLoading
равно true
, и тогда, когда это свойство равно false
.
Ещё одно улучшение, которое можно внести в этот код, основано на том, что, так как props.isLoading
— это логическое свойство, принимающее значение true
или false
, его можно использовать непосредственно, без применения оператора строгого сравнения его с true
. В результате получается следующее:
import React from "react"
function Conditional(props) {
return (
<div>
<h1>Navbar</h1>
{props.isLoading ? <h1>Loading...</h1> : <h1>Some cool stuff about conditional rendering</h1>}
<h1>Footer</h1>
</div>
)
}
export default Conditional
Теперь мы вышли на работающий пример использования технологии условного рендеринга, но тех же результатов можно добиться множеством способов. Например, обычно в компонентах, подобных нашему, не выводятся навигационные панели и «подвалы» страниц. Такие элементы страниц обычно выводятся либо самим компонентом App
, либо специальными компонентами, выводимыми компонентом App
.
Кроме того, надо отметить, что здесь вся логика условного рендеринга расположена внутри метода render()
функционального компонента, что сделано лишь для того, чтобы продемонстрировать компактный код, собранный в одном месте. Но, вероятно, ответственным за условный рендеринг стоило бы сделать компонент App
, а компонент, подобный нашему компоненту Conditional
, должен просто выводить на экран то, что ему передано. Если компонент App
ответственен за выяснение того, выполняется ли загрузка чего-либо в некий момент времени, и того, когда эта операция завершится, тогда он, скорее всего, должен быть ответственным и за определение того, что должно быть выведено на страницу. То есть, в нашем случае код можно было бы реорганизовать, выполнив проверку свойства isLoading
в методе render()
компонента App
и выведя на экран текст наподобие Loading...
в том случае, если загрузка не завершена, либо выведя компонент, подобный компоненту Conditional
в том случае, если загрузка завершилась. При этом компонент Conditional
вполне может и не принимать свойств от App
, выводя лишь то, что он, в любом случае, должен выводить.
Вот как выглядит код компонента App, преобразованный в соответствии с этими рассуждениями:
import React, {Component} from "react"
import Conditional from "./Conditional"
class App extends Component {
constructor() {
super()
this.state = {
isLoading: true
}
}
componentDidMount() {
setTimeout(() => {
this.setState({
isLoading: false
})
}, 1500)
}
render() {
return (
<div>
{this.state.isLoading ?
<h1>Loading...</h1> :
<Conditional />}
</div>
)
}
}
export default App
А вот — обновлённый код компонента Conditional
, в котором теперь нет проверки каких-либо условий:
import React from "react"
function Conditional(props) {
return <h1>Some cool stuff about conditional rendering</h1>
}
export default Conditional
Тут мы, правда, убрали навигационную панель и «подвал», но это в данном случае неважно.
Итоги
На этом занятии состоялось ваше первое знакомство с технологиями условного рендеринга. В следующий раз мы продолжим разбираться с ними. В частности, вас ждёт второе занятие по условному рендерингу и практическая работа по этой теме.
Уважаемые читатели! Если вы занимаетесь разработкой React-приложений — просим вас рассказать нам о том, как вы выполняете условный рендеринг.
Автор: ru_vds