Надо всех поймать!

Привет, хакеры! Я решил создать приложение React, используя Pokemon API, просто для удовольствия. Я подумал, что это будет отличный способ продемонстрировать, насколько мощен React, и будет интересным учебником для всех, кто только начинает. В этом блоге будет несколько частей, так как я также включу в серию стили. С учетом сказанного, давайте начнем ловить их всех.

Истории пользователей

Один из лучших советов, которые я получил, - это научиться «думать на React». Мы хотим получить хорошую структуру нашего приложения еще до того, как перейдем к строительной части. Для этого личного покедекса я хочу, чтобы пользователи могли видеть всех доступных покемонов. Затем я хочу, чтобы они могли переходить на личную страницу каждого покемона, чтобы просмотреть дополнительную информацию о нем, а также затем иметь возможность «поймать» указанного покемона. Наконец, как следует из названия проекта, я хочу, чтобы пользователь мог просматривать свой личный Pokedex, в котором есть все их «пойманные» покемоны.

Компонентное дерево

Теперь, когда у нас есть представление о том, что наше приложение должно делать для пользователей, давайте начнем «думать на React». React основан на компонентах. Вот официальная терминология React:

Концептуально компоненты похожи на функции JavaScript. Они принимают произвольные входные данные (называемые «реквизитами») и возвращают элементы React, описывающие, что должно появиться на экране.

Для этого проекта нам понадобится несколько разных компонентов и контейнеров. На мой взгляд, нам нужен AllPokemon.jscontainer, который будет отвечать за содержание отдельных компонентов, называемых PokemonCard.js. У каждого PokemonCard должен быть onClick прослушиватель, который позволит нам переходить на их личную страницу, которая будет компонентом с именем Pokemon.js. Наконец, нам понадобится Pokedex.js, который будет контейнером, который условно отображает только «пойманные» PokemonCard.

Create-React-App

Итак, у нас есть представление о том, как этот проект должен функционировать, и об общей структуре. Я поделюсь своим кодом внизу блога для тех, кто хотел бы писать код или проверять его по ходу дела. Начнем с create-react-app. Это отличный инструмент для быстрого запуска вашего проекта. create-react-app выполняет большую часть внутренней работы, чтобы помочь быстрее начать двигаться. Для получения дополнительной информации о том, что все это работает, вы можете ознакомиться с документацией здесь.

Первый шаг - запустить следующий код в желаемом месте:

npx create-react-app personal-pokedex

Как только это закончится, вы должны увидеть сообщение об успешном завершении в вашем терминале. Убедитесь, что вы cd personal-pokedex и находитесь в правильном каталоге. Как только вы это сделаете, вы можете запустить yarn start или npm start. Я буду использовать yarn команды терминала на протяжении всего проекта, но оба варианта работают, и это полностью личное предпочтение. Если все работает правильно, вы должны увидеть этот прекрасный экран:

Потрясающие!! Давайте удалим предоставленный шаблон, чтобы навести порядок. В App.js мы можем удалить кучу кода.

Мы можем удалить весь код, следующий за div в строке 6 предоставленного шаблона. Мы также можем удалить импорт как для логотипа, так и для файла CSS. Вы можете удалить оба файла из каталога, так как позже в этом руководстве мы напишем собственный CSS.

После того, как вы удалите все шаблоны, давайте добавим заголовок теста в наш div, чтобы убедиться, что все подключено правильно, и мы увидим, насколько мы классные.

Давайте также преобразуем наш вызов функции App.js в синтаксис ES6 с помощью стрелочной функции.

Мой App.js теперь выглядит так:

Я удалил App.css и файл логотипа из дерева файлов. Woot! А теперь поработаем!

Делаем наш первый контейнер

Хорошо, вспомните много месяцев назад, когда мы обсуждали дерево компонентов, давайте начнем делать этих плохих парней. Во-первых, в src я сделаю папку с именем containers. Ага, супер оригинал! Внутри containers я сделаю файл с именем AllPokemon.js. Я сделаю этот контейнер функциональным. GeeksForGeeks хорошо разбирается в различиях между функциональными и классовыми компонентами, эту информацию можно найти здесь. Ниже мой AllPokemon.js:

Я знаю, что вы не можете увидеть это в браузере, так что давайте подключим его! В App.js мы можем сделать наш первый импорт. Чтобы импортировать компонент, вы используете следующий синтаксис:

import ComponentName from './containers/AllPokemon

./ относится к пути к нашему файлу. Поскольку мы находимся в App.js, который просто болтается в нашем src директоре, мы должны подняться на один (./) в папку с контейнерами и найти AllPokemon.js. Вы можете не включать .js в вызов импорта, поскольку React решит это сам. После того, как вы импортировали файл, вы можете ссылаться на него в своем коде, используя самозакрывающийся тег, подобный этому <AllPokemon />.

Теперь вы должны увидеть что-то похожее на это:

В вашем браузере появилось как «Личный покедекс», так и «Я держу всех покемонов !!» должен отображаться. Бьюсь об заклад, вы хотите увидеть этих симпатичных покемонов! Так что поехали!

Получение в PokeApi

В папке src я собираюсь создать папку, в которой будут храниться наши служебные вызовы, чтобы мы могли получать данные за пределами наших контейнеров и компонентов. Я назову эту папку services, а внутри у меня будет файл, который будет отличаться для каждой выборки. Для нашего вызова AllPokemon fetch у меня будет файл с именем pokemons.js (да, я знаю, что множественное число от Pokemon - это просто Pokemon, я тоже его ненавижу, но я не знаю, как еще его назвать!). Я начну с объявления URL как const в верхней части файла, а затем сделаю запрос на выборку. Документация по загрузке действительно великолепна, и я связал ее здесь для тех из вас, кто хотел бы получить больше информации. Я также написал блог о Fetch, ссылка на который находится здесь.

Вот взгляд на наш первый запрос на выборку:

Мы экспортируем getPokemons запрос на выборку и теперь можем импортировать его в другие компоненты и контейнеры. Так что давайте сделаем это. В AllPokemon.js нам нужно импортировать несколько вещей, чтобы этот контейнер работал и загружался. Прежде всего, поскольку это функциональный компонент, мы будем использовать React Hooks для выполнения наших вызовов выборки. Это было сделано намеренно, поскольку я собираюсь использовать хуки на протяжении всего урока. Итак, давайте импортируем следующее:

import React, { useState, useEffect } from 'react';

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

import { getPokemons } from '../services/pokemons';

На этом мы готовы приступить к написанию кода!

Использование полученных данных

Первый шаг к доступу к нашим данным - через ловушку useEffect. Обработчик эффектов позволяет нам обрабатывать все, что может вызвать побочные эффекты. Считается, что функция имеет побочный эффект, если она пытается изменить что-либо за пределами своего тела, но что она изменяет? Что ж, мы знаем, что нам понадобится доступ ко всем нашим покемонам, так что давайте useState! Для более подробной документации по React Hooks, пожалуйста, посетите docs.

Хук useState возвращает пару значений: текущее состояние и функцию, которая его обновляет. Текущее состояние в нашем случае будет pokemons, а функция, которая его обновляет, будет setPokemons. Наш useState вызов будет выглядеть так:

const [pokemons, setPokemons] = useState([]);

Мы передаем пустой массив в ловушку useState, чтобы установить значение по умолчанию для нашей переменной состояния pokemons.

Затем нам нужно будет использовать ловушку useEffect, чтобы получить наши данные из API. Если вы знакомы с компонентами на основе классов, useEffect работает примерно так же, как ComponentDidMount или ComponentDidUpdate. Цель состоит в том, чтобы обновить наше состояние информацией, которую мы получаем из PokeApi. Во-первых, давайте посмотрим на данные, поступающие через консоль. Обратите внимание на пустой массив после вызова useEffect. Обратите внимание, что useEffect работает как для установки, так и для обновления компонентов. Если вы не передадите массив зависимостей в useEffect, он будет постоянно обновляться. Если вы передадите в вызов пустой массив зависимостей, он будет запускаться только один раз при монтировании компонента.

Вот что показывает моя консоль! 20 покемонов вложены в объект!

Вау! Мы сделали это. У нас есть ПОКЕМОН! У нас всего 20, так как PokeAPI разработан с разбивкой на страницы, но я думаю, что 20 - это хорошее число, с которым можно поиграть. Позже мы сможем выяснить, как получить доступ к большему количеству покемонов.

Хорошо, теперь нам нужно обработать нашу выборку внутри хука useEffect. Давайте вызовем setPokemons внутри ловушки и передадим в него эти данные. Помните, что setPokemons - это функция, которую мы передали ловушке setState, которая отвечает за обновление нашей переменной состояния pokemons. Итак, теперь мы можем console.log pokemons и должны увидеть наш объект из API! Так здорово! Вот только взгляните на AllPokemon.js:

Давай рендерим покемонов!

Первое, что нам нужно сделать, это создать компонент, который мы обсуждали ранее, PokemonCard.js. Я создал папку с названием (как вы уже догадались) components в папке src и добавил файл PokemonCard.js. Цель этого компонента состоит в том, чтобы он отображался для каждого покемона, который есть в наших результатах. Возможность многократного использования компонентов - одна из ключевых особенностей React.

Если вы внимательно посмотрите на результаты нашего console.log(pokemons), вы увидите, что мы получаем объект обратно. Javascript позволяет нам анализировать объекты с помощью метода Object.entries(), в котором мы передаем объект в качестве аргумента. Вы можете console.log(Object.entries(pokemons) увидеть, к каким данным у вас есть доступ сейчас. В консоли вы должны увидеть 4 массива, один из которых указывает на массив с именем results. Это то, что нам нужно. Он всегда имеет индекс 3. Давайте попробуем другой console.log.

console.log(Object.entries(pokemons)[3])

Теперь мы должны видеть только массив результатов, но подождите, покемоны будут дополнительно вложены в массив с индексом 1.

console.log(Object.entries(pokemons)[3][1])

Теперь вы можете действительно получить результаты, которые ищете, но обновите страницу, и все это сломается. Какого черта? Мы видели результаты, но теперь не можем до них добраться. Помните, что обновления состояния происходят асинхронно, что означает, что обновление состояния не мешает остальной части приложения отрисовывать и работать плавно. Было бы плохо, если бы все приложение перестало работать, пока мы забирали покемонов. Никто не захочет использовать это. Итак, чтобы исправить эту ошибку, мы должны выполнить так называемое короткое замыкание. По сути, мы должны сначала проверить, заполнен ли объект покемона, а затем мы можем вызвать его. Вот отличный блог, в котором это обсуждается более подробно.

console.log(Object.entries(pokemons)[3] && Object.entries(pokemons)[3][1])

ДА. Успешно, мы наконец получили доступ к нашему массиву покемонов. Теперь мы можем использовать надежный .map метод для рендеринга наших PokemonCard компонентов. Ниже представлен полный AllPokemon.js компонент с .map методом, реализованным на JSX.

Хорошо! Мы сделали это, ребята. Переходим на PokemonCard.js, чтобы фактически отображать имена покемонов. В нашем PokemonCard.js у нас теперь есть доступ к props, благодаря которому React позволяет нам динамически обмениваться информацией между родительскими и дочерними компонентами. Выше мы разложили (…) наш массив покемонов и сделали его копию, которая будет использоваться в качестве свойств в нашем компоненте. Обязательно передайте в компонент свойства const PokemonCard = (props) => {}, а затем попробуйте console.log(props). Вы должны увидеть своего покемона. Как видите, здесь есть два ключа: имя и URL. Давайте обновим этот console.log:

console.log(props.name)

Вы должны увидеть список имен покемонов в консоли. Используя JSX, давайте динамически визуализируем этих покемонов. Я сделаю элемент h3 для отображения имени покемона. Ниже мой PokemonCard компонент:

Подождите, никаких симпатичных картинок с покемонами. Это не выдержит! К сожалению, у PokeAPI нет изображений, но есть другой API. Мы можем установить src тега img на следующее:

src={`https://pokeres.bastionbot.org/images/pokemon/${props.id}.png`}

Подождите, у нас нет prop id. Давайте передадим индекс как опору. К счастью для нас, покемонам присваиваются идентификаторы в зависимости от того, в каком порядке они были обнаружены. Таким образом, первые 151 покемон имеют заказ от 1 до 151, и каждый последующий покемон следует этому примеру. Итак, мы можем просто передать индекс как опору id и добавить 1, потому что нулевого покемона нет. После передачи опоры AllPokemon.js добавляет еще одну опору к нашему PokemonCard:

return <PokemonCard key={index} {...pokemon} id={index + 1} />

Теперь мы можем создать тег img и использовать указанный выше src. Теперь наш компонент PokemonCard должен выглядеть так:

Ух ты, столько милых покемонов! Теперь изображения слишком большие и довольно простые. Я буду работать над стилизацией в следующем блоге. Цель состоит в том, чтобы получить милые маленькие карточки с покемонами, на которые можно нажимать. Увидимся на следующей неделе, надеюсь, вам понравилось!

Вот ссылка на репо. Вот ссылка на все сущности, использованные в этом руководстве.