Главная » Хабрахабр » [Перевод] Учебный курс по React, часть 11: динамическое формирование разметки и метод массивов map

[Перевод] Учебный курс по React, часть 11: динамическое формирование разметки и метод массивов map

В сегодняшней части перевода учебного курса по React мы поговорим об использовании стандартного метода массивов map() для организации динамического формирования JSX-разметки, описывающей наборы однотипных элементов.

image

→ Часть 1: обзор курса, причины популярности React, ReactDOM и JSX
→ Часть 2: функциональные компоненты
→ Часть 3: файлы компонентов, структура проектов
→ Часть 4: родительские и дочерние компоненты
→ Часть 5: начало работы над TODO-приложением, основы стилизации
→ Часть 6: о некоторых особенностях курса, JSX и JavaScript
→ Часть 7: встроенные стили
→ Часть 8: продолжение работы над TODO-приложением, знакомство со свойствами компонентов
→ Часть 9: свойства компонентов
→ Часть 10: практикум по работе со свойствами компонентов и стилизации
→ Часть 11: динамическое формирование разметки и метод массивов map

Занятие 21. Динамическое формирование разметки и метод массивов map

→ Оригинал

Напомним, что тогда код файла App.js выглядел следующим образом: Продолжим работу с того момента, на котором мы остановились, выполняя предыдущее практическое задание.

import React from "react" import Joke from "./Joke" function App() { return ( <div> <Joke punchLine="It’s hard to explain puns to kleptomaniacs because they always take things literally." /> <Joke question="What's the best thing about Switzerland?" punchLine="I don't know, but the flag is a big plus!" /> <Joke question="Did you hear about the mathematician who's afraid of negative numbers?" punchLine="He'll stop at nothing to avoid them!" /> <Joke question="Hear about the new restaurant called Karma?" punchLine="There’s no menu: You get what you deserve." /> <Joke question="Did you hear about the actor who fell through the floorboards?" punchLine="He was just going through a stage." /> <Joke question="Did you hear about the claustrophobic astronaut?" punchLine="He just needed a little space." /> </div> )
} export default App

Компонент App выводит набор компонентов Joke. Вот как на данном этапе работы выглядит страница приложения.

Страница приложения

Сейчас значения этих свойств заданы в коде создания экземпляров компонента Joke в виде обычного текста. Некоторым из этих компонентов передаются свойства question и punchLine, а некоторым — только punchLine. Эти API поддерживаются средствами серверов, которые берут информацию из баз данных, оформляют её в виде JSON-кода и отправляют этот код клиентским частям приложений. В реальности же основной объём данных, которые выводят на страницы React-приложений, поступает в приложение в результате выполнения HTTP-запросов к неким API. А именно, это будет файл jokesData.js со следующим содержимым: Мы пока ещё не дошли до такого уровня, чтобы выполнять запросы к API, поэтому сейчас мы, в роли источника данных, воспользуемся файлом с данными, которые могли бы быть получены в результате разбора JSON-ответа сервера.

const jokesData = [ , { id: 2, question: "What's the best thing about Switzerland?", punchLine: "I don't know, but the flag is a big plus!" }, { id: 3, question: "Did you hear about the mathematician who's afraid of negative numbers?", punchLine: "He'll stop at nothing to avoid them!" }, { id: 4, question: "Hear about the new restaurant called Karma?", punchLine: "There’s no menu: You get what you deserve." }, { id: 5, question: "Did you hear about the actor who fell through the floorboards?", punchLine: "He was just going through a stage." }, { id: 6, question: "Did you hear about the claustrophobic astronaut?", punchLine: "He just needed a little space." }
] export default jokesData

Этот файл будет расположен в директории src нашего проекта.

Новый файл в папке src

Похожий массив можно получить, разобрав JSON-данные, полученные от некоего API. Фактически, в нём содержится массив объектов. При необходимости мы можем импортировать этот файл в компонент, в котором он нужен, и представить себе, что работаем мы не с данными, взятыми из файла, а с тем, что вернуло нам некое API. Мы экспортируем из этого файла массив jokesData.

Теперь, когда у нас есть массив исходных данных, подумаем о том, как превратить эти данные в набор экземпляров React-компонентов.

Причина подобного заключается в том, что действия, подобные тому, о котором мы будем сейчас говорить, в других фреймворках, вроде Angular и Vue, выполняются с помощью каких-то особых средств. Многие разработчики говорят о том, что благодаря освоению React они лучше изучили JavaScript. А в React подобное делается с помощью обычного JavaScript.

Эти методы могут, в качестве аргументов, принимать функции, описываемые программистами. В частности, мы планируем пользоваться некоторыми стандартными методами массивов, являющимися функциями высшего порядка. Именно эти функции определяют то, что вызов того или иного стандартного метода сделает с элементами массива.

Предположим, у нас есть числовой массив:

const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Мы можем обработать этот массив с помощью стандартного метода массивов map(), передав ему некую функцию, которая задаёт порядок преобразования элементов этого массива. В нашем случае этой функции будут передаваться, по одному, числа из этого массива. Функция может делать с ними всё, что угодно, после чего то, что она возвратит, попадёт в новый массив, в элемент, индекс которого соответствует индексу обрабатываемого элемента. Если нам нужно сформировать новый массив, элементы которого представляют собой элементы исходного массива, умноженные на 2, то выглядеть это будет так:

const doubled = nums.map(function(num) { return num * 2
})

Проверим работу этого кода:

console.log(doubled) // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

Если раньше вы не встречались с методами массивов — такими, как map(), filter(), reduce() и другие — рекомендуется с ними разобраться.

Здесь мы, для автоматического формирования списка экземпляров компонента, будем пользоваться методом map().

Импортируем в файл App.js файл jokesData.js. Вернёмся к нашему примеру. Делается это так:

import jokesData from "./jokesData"

После этого, в коде программы, мы сможем работать с массивом jokesData. А именно, мы собираемся воспользоваться методом map(). Вот как будет выглядеть «заготовка» этого метода.

jokesData.map(joke => { })

Обратите внимание на то, что мы здесь передаём методу map() стрелочную функцию. В нашем случае это позволяет сделать код компактнее. Так как функция принимает всего один параметр (joke), мы, при её объявлении, можем обойтись без круглых скобок.

Вот как это может выглядеть: Из функции, передаваемой методу map(), мы хотим вернуть новый экземпляр компонента Joke, которому переданы свойства question и punchLine поступившего в неё элемента массива jokesData.

jokesData.map(joke => { return ( <Joke question={joke.question} punchLine={joke.punchLine} /> )
})

Этот код можно сократить, если учесть два факта. Во-первых, return возвращает лишь один элемент, поэтому можно поместить этот элемент сразу после return, избавившись от круглых скобок. Во-вторых, стрелочная функция содержит лишь операцию возврата некоего значения, поэтому при объявлении такой функции можно обойтись и без ключевого слова return и без фигурных скобок. Кроме того, вспомним о том, что в результате работы метода map() формируется новый массив. Этот массив нужно где-то сохранить. Все эти рассуждения приводят нас к следующему:

const jokeComponents = jokesData.map(joke => <Joke question={joke.question} punchLine={joke.punchLine} />)

В константе jokeComponents теперь будет содержаться массив, каждый элемент которого представляет собой описание экземпляра компонента Joke с переданными ему свойствами question и punchLine.

React позволяет очень удобно работать с такими массивами. Что нам теперь делать с этим массивом компонентов? Вот как теперь будет выглядеть код файла App: А именно, речь идёт о том, что такой массив можно использовать в JSX-коде.

import React from "react" import Joke from "./Joke"
import jokesData from "./jokesData" function App() { const jokeComponents = jokesData.map(joke => <Joke question={joke.question} punchLine={joke.punchLine} />) return ( <div> {jokeComponents} </div> )
} export default App

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

Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `App`. See https://fb.me/react-warning-keys for more information. in Joke (at App.js:7) in App (at src/index.js:6)

Смысл его сводится к тому, что у элементов массива должно быть уникальное свойство key. Мы не будем вдаваться в подробности, касающиеся того, почему React ожидает наличия уникального свойства key у повторяющихся компонентов. Нам достаточно учесть тот факт, что выполняя операции массового создания экземпляров компонентов, наподобие той, которую мы только что выполняли с помощью метода map(), экземплярам нужно передавать свойство key. При этом такое свойство можно передать и самому экземпляру компонента, и, например, тегу <div>, в который заключён код компонента. Это особой роли не играет.

Как правило, в объектах данных, получаемых из API, имеются некие идентификаторы (свойства наподобие id). Итак, свойству key нужно назначить некое уникальное значение. Например, мы могли бы назначить свойству key значение joke.question — все тексты в этих свойствах в нашем приложении уникальны. Главное для нас — их уникальность. Вспомните о том, как выглядят объекты с данными из массива, который мы экспортировали из файла jokesData.js. Но мы поступим иначе. Вот его фрагмент:

const jokesData = [ { id: 1, punchLine: "It’s hard to explain puns to kleptomaniacs because they always take things literally." }, { id: 2, question: "What's the best thing about Switzerland?", punchLine: "I don't know, but the flag is a big plus!" },
... ]

У каждого объекта есть свойство id, уникальность которого мы поддерживаем самостоятельно. Именно значения таких свойств и можно использовать в роли значений для свойства key.

Теперь код создания массива экземпляров компонента в App.js примет следующий вид:

const jokeComponents = jokesData.map(joke => <Joke key={joke.id} question={joke.question} punchLine={joke.punchLine} />)

Если внести это изменение в код, взглянуть на страницу приложения в браузере и проверить содержимое консоли, то окажется, что уведомление о свойстве key исчезло.

Однако код компонента App стал гораздо короче и понятней, а данные для формирования списка компонентов берутся теперь из чего-то, сильно напоминающего внешний источник данных. После всех преобразований, которым мы подвергли проект, внешний вид страницы приложения не изменился. Именно по такой схеме работают настоящие приложения.

Мы пользовались методикой формирования списка экземпляров компонента Joke в компоненте App, но ничто не мешает нам, при необходимости, воспользоваться таким же подходом и в компоненте Joke, который может, на основе переданных ему данных, формировать собственный список экземпляров некоего компонента. Кроме того надо отметить, что ядром вышеописанной модификации кода стало использование стандартного метода массивов map().

Например, метод sort() можно использовать для сортировки элементов массивов по некоему признаку. При этом, как мы уже говорили, среди стандартных методов массивов можно найти и другие интересные инструменты. Всё это применимо и при работе с массивами, содержащими экземпляры компонентов. Метод filter() можно использовать для отбора только тех элементов массива, которые соответствуют неким критериям.

Скажем, попытайтесь воспользоваться методом filter() и убрать из вывода, формируемого компонентом App, те экземпляры компонента Joke, свойство question которых не превышает заданную длину. Если хотите — можете поэкспериментировать с этими методами. Или сделайте так, чтобы в вывод попали бы только компоненты, для которых задано и свойство question, и свойство punchLine.

Итоги

Сегодня мы поговорили об использовании стандартного метода массивов map() для формирования списков компонентов, а также обсудили возможности, которые даёт нам использование других стандартных методов массивов. В следующий раз вас ждёт практическое занятие по изученному сегодня материалу.

Уважаемые читатели! Как бы вы подошли к решению задачи по выводу компонентом App только тех экземпляров компонента Joke, длина значения свойства question которых превышает заданную длину?


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

[Перевод] Конфигурируйте Visual Studio в вашей организации с помощью .vsconfig

В Visual Studio 2017 Update 15.9 мы добавили возможность экспорта и импорта рабочей нагрузки и выбора компонентов в файл конфигурации установки Visual Studio. Разработчики могут импортировать эти файлы в новые или существующие установки. Проверка этих файлов в ваших исходных репозиториях ...

Тест-драйв nanoCAD СПДС Стройплощадка 8. Часть 1

Тест-драйв nanoCAD СПДС Стройплощадка 8 Мы начинаем публикацию тест-драйва по nanoCAD СПДС Стройплощадка. В первой части тест-драйва мы разберем работу со специальным инструментом Менеджер проектов, его основные функции и способы построения элементов стройгенплана. Во второй части тест-драйва мы рассмотрим построение ...