Хабрахабр

[Перевод] Учебный курс по React, часть 20: первое занятие по условному рендерингу

Сегодня, в переводе следующей части учебного курса по React, мы поговорим об условном рендеринге.

image

→ Часть 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, 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, имеется компонент, основанный на классе. Поговорим о коде, с которым мы сейчас будем экспериментировать. Подобная конструкция часто применяется в случаях, когда для приведения компонента в рабочее состояние нужно, например, выполнять запросы к некоему API, и, пока компонент ожидает поступления данных и разбирает их, нужно что-то показать на экране. В его конструкторе инициализировано состояние, содержащее свойство isLoading, установленное в значение true. В результате в состоянии есть свойство, которое указывает на то, выполняет ли в настоящий момент приложение некие служебные действия. Возможно, на выполнение обращения к API требуется 3-4 секунды, и вы не хотите, чтобы пользователь, глядя на экран, думал бы, что ваше приложение дало сбой. И условный рендеринг будет использоваться для вывода на экран чего-то, что сообщает пользователю о том, что приложение в настоящий момент что-то загружает в фоне.

Пока же обратим внимание на метод render(). В коде компонента App есть метод componentDidMount(), который мы уже совсем скоро обсудим. Этому компоненту передаётся свойство isLoading, представляющее собой текущее значение свойства isLoading из состояния компонента App. Здесь мы выводим компонент Condition, который импортирован в коде, находящемся в верхней части файла App.js. Пока же давайте вернёмся к методу componentDidMount() из кода компонента App. Код компонента Conditional пока не возвращает ничего такого, что можно вывести на экран, этим компонентом мы займёмся немного позже.

В коде этого метода мы имитируем обращение к некоему API. Вспомните о том, что метод componentDidMount() даёт нам возможность выполнять некий код сразу после того, как компонент, в нашем случае это компонент App, впервые будет выведен на экран. Когда это время пройдёт — будет запущен код функции, переданной функции setTimeout(). Здесь мы устанавливаем таймер на полторы секунды. А именно, его свойство isLoading устанавливается в значение false. В этой функции, исходя из предположения о том, что её вызов символизирует окончание загрузки данных из API, выполняется изменение состояния. На будущих занятиях мы поговорим об использовании функции fetch() для загрузки данных, пока же ограничимся вышеописанной имитацией этого процесса. Это говорит о том, что загрузка данных завершена, и приложение после этого может нормально работать.

Дело в том, что как только свойство состояния isLoading меняется с true на false, компонент Conditional получает новое значение свойства. Кстати, тут будет уместно ещё раз поднять тему методов жизненного цикла компонента. Собственно говоря, при изменении состояния повторно вызывается метод render(), в результате компонент Conditional также будет повторно выведен на экран. Сначала, при первом выводе компонента на экран, он получает, в свойстве isLoading, значение true, а затем, после того, как состояние меняется, он получает то же свойство с новым значением. Но то, что мы возвращаем из этой функции при повторном рендеринге компонента, может отличаться от того, что возвращалось ранее. Напомним о том, что Conditional — это обычный функциональный компонент, то есть его повторный рендеринг означает повторный вызов функции, которой он представлен. Причиной такого изменения является изменение того, что мы передаём компоненту.

Прежде чем мы приступим к работе над кодом, проверим, работают ли те механизмы, которые в нём уже имеются. Итак, компонент Conditional принимает свойство isLoading. После этого код компонента будет выглядеть так: Для этого мы вернём из компонента некую разметку и выведем в консоль значение props.isLoading.

import React from "react" function Conditional(props) { console.log(props.isLoading) return ( <h1>Temp</h1> )
} export default Conditional

Страница приложения после этого будет выглядеть так, как показано на следующем рисунке.

Страница приложения в браузере

5 секунды. Обратите внимание на то, что true выводится в консоль сразу же после загрузки приложения, а false — через 1. Это происходит благодаря работе вышеописанного механизма в методе componentDidMount() компонента App.

Его суть заключается в том, что мы выводим что-то на экран только в том случае, если выполняется некое условие. Теперь поговорим об условном рендеринге. Если же это значение равно false, что символизирует окончание загрузки, из компонента можно вернуть какой-нибудь другой текст. В данном случае, вместо вывода на страницу строки Temp, мы, в компоненте Conditional, можем проверить значение props.isLoading, и, если оно равно true, вывести на страницу текст Loading.... В коде это будет выглядеть так:

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&gtl;. Для того, чтобы это исправить, вспомним о том, что 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.

Но, вероятно, ответственным за условный рендеринг стоило бы сделать компонент App, а компонент, подобный нашему компоненту Conditional, должен просто выводить на экран то, что ему передано. Кроме того, надо отметить, что здесь вся логика условного рендеринга расположена внутри метода render() функционального компонента, что сделано лишь для того, чтобы продемонстрировать компактный код, собранный в одном месте. То есть, в нашем случае код можно было бы реорганизовать, выполнив проверку свойства isLoading в методе render() компонента App и выведя на экран текст наподобие Loading... в том случае, если загрузка не завершена, либо выведя компонент, подобный компоненту Conditional в том случае, если загрузка завершилась. Если компонент App ответственен за выяснение того, выполняется ли загрузка чего-либо в некий момент времени, и того, когда эта операция завершится, тогда он, скорее всего, должен быть ответственным и за определение того, что должно быть выведено на страницу. При этом компонент 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-приложений — просим вас рассказать нам о том, как вы выполняете условный рендеринг.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть