Это последняя часть урока! В этой части мы будем управлять состоянием и будем получать данные из poke api. Наконец, мы будем использовать данные Pokemon для заполнения DetailView. Для начала клонируйте ветку part5 этого репо.

Проходящие события

Нам нужно создать событие щелчка для каждого из наших PokeCell, чтобы получить данные Pokemon из poke api. Для этого сначала мы создадим функцию как часть класса App, а затем передадим ее в качестве реквизита каждому из наших PokeCell.

Откройте файл App.js и создайте функцию handleOnClick с параметром id. В теле функции включите console.log аргумента id.

import React, { Component } from 'react';
import PokeList from './PokeList';
import DetailView from './DetailView';
import './styles/App.css';

class App extends Component {
  constructor() {
    super();
    this.state = {};
  }


  handleOnClick(id) {
    console.log(id);
  }


  render() {
    return (
      <div className="App">
        <PokeList />
        <DetailView />
      </div>
    );
  }
}

export default App;

Поскольку мы передаем вниз эту функцию в PokeCells, нам нужно сначала передать ее через компонент PokeList.

Добавьте новую опору в PokeList с именем handleOnClick и передайте только что созданную функцию handleOnClick.

  render() {
    return (
      <div className="App">
        <PokeList handleOnClick={this.handleOnClick} />
        <DetailView />
      </div>
    );
  }

Мы включили ключевое слово this, поскольку handleOnClick является методом класса App. Вот как вы обычно передаете функции как свойства из компонента класса. Однако мы потеряем контекст this, когда попытаемся использовать эту функцию в компоненте PokeList. Чтобы решить эту проблему, нам нужно привязать контекст приложения к методу handleOnClick.

В функции-конструкторе используйте метод bind для привязки handleOnClick к текущему классу:

class App extends Component {
  constructor() {
    super();
    this.state = {};

    this.handleOnClick = this.handleOnClick.bind(this);
  }

Теперь при инициализации метод всегда будет иметь контекст приложения в других компонентах.

Откройте файл PokeList и выполните следующие действия:

  1. Деконструируйте handleOnClick из аргументов PokeList.
  2. Добавьте метод handleOnClick как опору PokeCells.
const PokeList = ({ handleOnClick }) => {
  const cells = pokeClasses.map(pokeClass => {
    return (
      <PokeCell 
        key={pokeClass.id} 
        pokeClass={pokeClass} 
        handleOnClick={handleOnClick}
      />
    );
  });


  return (
    <section className="poke-list">
      {cells}
    </section>
  )
}

Откройте файл PokeCell и выполните следующие действия:

  1. Деконструируйте handleOnClick из аргументов PokeCell.
  2. Добавьте событие onClick внутри вкладки кнопки и назначьте ему анонимную функцию, которая вызывает handleOnClick с переменной id в качестве параметра.

Теперь каждый раз, когда мы нажимаем кнопку PokeCell, идентификатор выбранного покемона будет регистрироваться в консоли.

import React from 'react';
import sprites from '../assets/sprites.png';
import './styles/PokeCell.css';

const PokeCell = ({ pokeClass, handleOnClick }) => {
  const { id, backgroundPosition } = pokeClass;
  const style = { backgroundImage: `url(${sprites})`, backgroundPosition };

  return <button onClick={() => handleOnClick(id)} style={style} className="poke-cell"></button>
};

export default PokeCell;

Запустите приложение и щелкните PokeCell. Вы сможете увидеть идентификатор покемона, которого щелкнули в консоли.

Покемон Помощник

Мы собираемся создать класс Pokemon для очистки данных из API, которые мы собираемся получить. Таким образом, мы можем легче управлять данными покемонов.

В каталоге src создайте файл Pokemon.js.

touch Pokemon.js

Откройте файл Pokemon.js и добавьте следующий код:

class Pokemon {
  constructor(data) {
    this.id = data.id;
    this.name = data.name;
    this.sprite = data.sprites.front_default;
    this.type = data.types[0].type.name;
  }
}

export default Pokemon;

Теперь, когда мы получаем данные, мы можем создать новый объект Pokemon и передать ему полученные данные.

Получение данных

Откройте файл App.js и выполните следующие действия:

  1. Импортируйте класс Pokemon в начало файла.
  2. В методе handleOnClick сделайте запрос на выборку в poke api. Используйте строковую интерполяцию, чтобы добавить маршрут динамического URL-адреса с аргументом id.
  3. Выполните обещание получения и создайте новый экземпляр Pokemon с полученными данными.
  4. Добавьте console.log объекта Pokemon, чтобы видеть данные Pokemon при нажатии PokeCell.
import React, { Component } from 'react';
import PokeList from './PokeList';
import DetailView from './DetailView';
import Pokemon from '../Pokemon';
import './styles/App.css';
// Inside the App class
handleOnClick(id) {
    fetch(`http://pokeapi.co/api/v2/pokemon/${id}/`)
      .then(res => res.json())
      .then(data => {
        const pokemon = new Pokemon(data);

        console.log(pokemon);
      })
      .catch(err => console.log(err));
  }

Состояние

Теперь, когда мы успешно получили данные о наших покемонах, нам нужно передать эти данные в DetailView, чтобы отобразить их.

Для этого нам сначала нужно извлечь объект Pokemon из разрешенного обещания и сохранить его в состоянии нашего приложения.

В функции конструктора компонента App добавьте новый ключ к объекту состояния со значением пустого объекта.

constructor() {
    super();
    this.state = {
      pokemon: {}
    };

    this.handleOnClick = this.handleOnClick.bind(this);
  }

Чтобы обновить состояние покемона, React предоставляет нам функцию setState. Эта функция принимает объект в качестве аргумента, где нам нужно указать ключ, который мы хотим обновить, и присвоить ему новое значение. Каждый раз, когда вызывается эта функция, React повторно отображает все дочерние компоненты.

Чтобы добавить данные Pokemon в состояние, замените console.log функцией setState внутри функции handleOnClick и передайте ей новый объект Pokemon, который мы создали.

handleOnClick(id) {
    fetch(`http://pokeapi.co/api/v2/pokemon/${id}/`)
      .then(res => res.json())
      .then(data => {
        const pokemon = new Pokemon(data);

        this.setState({ pokemon });
      })
      .catch(err => console.log(err));
  }

Теперь, когда мы сохранили данные о наших покемонах в состоянии приложения, мы можем получить к ним доступ в функции рендеринга.

Передайте состояние покемона компоненту DetailView как опору. Поскольку свойство состояния является частью класса App, нам нужно включить ключевое слово this для доступа к нему.

render() {
    return (
      <div className="App">
        <PokeList handleOnClick={this.handleOnClick} />
        <DetailView pokemon={this.state.pokemon} />
      </div>
    );
  }

Отображение данных

Откройте файл DetailView.js и выполните следующие действия:

  1. Деконструируйте покемона из аргументов DetailView.
  2. Деконструируйте идентификатор, имя, спрайт и тип из переменной pokemon.
  3. В теге изображения добавьте атрибут src и назначьте ему переменную спрайта, заключенную в фигурные скобки.
  4. Между тегами h1 добавьте переменные id и name, заключенные в фигурные скобки.
  5. В теге p добавьте переменную типа, заключенную в фигурные скобки.
import React from 'react';
import './styles/DetailView.css';

const DetailView = ({ pokemon }) => {
  const { id, name, sprite, type } = pokemon;

  return (
    <section className="detail-view">
      <img src={sprite} className='sprite-image' alt="sprite"/>
      <div className='data-wrapper'>
        <h1 className='data-name'>ID: {id} {name}</h1>
        <p className="data-char">Type: {type}</p>
      </div>
    </section>
  )
}

export default DetailView;

Мы сделали! Дайте себе пять!

Идите вперед и запустите приложение, чтобы увидеть результат.

Нажмите на своего любимого покемона и просмотрите информацию о нем.