# Асинхронные запросы

Нам все еще не нужен redux, ничего подобного.

CRA так устроен, что если вы положите что-нибудь в public директорию, это будет доступно по пути:

```
http://localhost:3000/название-директории/название-файла (но без слова public в пути)
```

Переместим наш json с новостями в *src/public/data/newsData.json*

Теперь его можно открыть GET-запросом на [localhost:3000/data/newsData.json](http://localhost:3000/data/newsData.json)

![news in json](https://2647972981-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LvLmy8qHKus4tjYXHI4%2F-LvLmyreyxcNGjR6qn1l%2F-LvLnSrBI3ez16GEowCt%2Fnews-in-json.jpg?generation=1575561866294780\&alt=media)

Конечно, при этом у нас сломался импорт в *App.js* (так как такого файла по старому пути нет):

![news failed to compile](https://2647972981-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LvLmy8qHKus4tjYXHI4%2F-LvLmyreyxcNGjR6qn1l%2F-LvLnSrDbrWLUNssBO4Q%2Fnews-failed-to-compile.jpg?generation=1575561865911050\&alt=media)

Так как у нас есть доступ к файлу через GET-запрос, мы можем запросить его.

"Давайте представим задачу" (c)

У нас есть данные на сервере (новости в json), нам нужно их запросить и отобразить в списке. Пока запрос выполняется, мы хотим показывать юзеру надпись: "Загружаю..." вместо списка новостей, чтобы он не нервничал. Когда новости загружены - мы хотим отобразить их как раньше.

Что нового в этой задаче:

* как сделать асинхронный запрос (вопрос не про react) \[1];
* где делать асинхронный запрос (про react) \[2];

\[1] - это вопрос про нативный js. Запрос будем делать с помощью [fetch](https://developer.mozilla.org/ru/docs/Web/API/Fetch_API/Using_Fetch).

\[2] - запрос за данными следует начать в *componentDidMount*

Начнем с подготовки "состояния" и шаблона.

*src/App.js*

```jsx
import React from 'react'
import { Add } from './components/Add'
import { News } from './components/News'
// удален импорт newsData
import './App.css'

class App extends React.Component {
  state = {
    news: null, // было newsData
    isLoading: false, // статус для манипуляций "прелоадером" ("Загружаю..." в нашем случае)
  }
  ...
}

export default App
```

В данный момент наше приложение не работает, но мы приготовили несколько важных вещей:

* во-первых, мы сможем на основе данных в newsData сказать:
  * если `newsData: null`:
    * если `isLoading: false` - значит данные еще не были загружены или произошла ошибка;
    * если `isLoading: true` - данные еще загружаются
  * если `newsData: []` (пустой массив) - значит новостей нет;
  * если \`newsData: \[данные про новости] - значит новости есть и они загружены;

В реакт-приложениях, все начинается с представления (описания) данных. Рисуйте в голове или на листочке, так как на основе такой шпаргалки, нам не составит труда сделать шаблон.

Начнем с составления выражений для шаблона. Первое:

```
{Array.isArray(news) && <News data={news} />}
```

То есть мы проверяем, если в `this.state.news` - массив - то рисуй компонент новости, он уже умеет рисовать "новостей нет" или список новостей.

Документация про [isArray](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) (MDN)

Второе условие:

```
{isLoading && <p>Загружаю...</p>}
```

Оформим все это в компоненте:

*src/App.js*

```jsx
class App extends React.Component {
  state = {
    news: null,
    isLoading: false,
  }
  handleAddNews = data => {
    const nextNews = [data, ...this.state.news]
    this.setState({ news: nextNews })
  }
  render() {
    const { news, isLoading } = this.state // все необходимое взяли из state

    return (
      <React.Fragment>
        <Add onAddNews={this.handleAddNews} />
        <h3>Новости</h3>
        {isLoading && <p>Загружаю...</p>}
        {Array.isArray(news) && <News data={news} />}
      </React.Fragment>
    )
  }
}
```

Осталось сделать асинхронный вызов и установить правильное состояние.

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

Вернемся к коду и сделаем fetch-запрос + console.log'и. Как я уже говорил, запрос за данными будем делать в момент, когда компонент уже примонтирован (то есть появился на странице, то есть нам нужен метод жизненного цикла - componentDidMount):

*src/App.js*

```jsx
class App extends React.Component {
  ...
  componentDidMount() {
    fetch('http://localhost:3000/data/newsData.json')
      .then(response => {
        return response.json()
      })
      .then(data => {
        console.log(this)
        console.log('приехали данные ', data)
      })
  }
  ...
}
```

Посмотрите в network, все работает:

![news network](https://2647972981-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LvLmy8qHKus4tjYXHI4%2F-LvLmyreyxcNGjR6qn1l%2F-LvLnSrGUzR3sY11kT31%2Fnews-network.jpg?generation=1575561866042764\&alt=media)

Заглянем в console, и увидим, что так как мы используем стрелочные функции - мы не потеряли `this`.

![this equal app in fetch](https://2647972981-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LvLmy8qHKus4tjYXHI4%2F-LvLmyreyxcNGjR6qn1l%2F-LvLnSrITSCMDTm-Jyz8%2Fthis-equal-app-in-fetch.jpg?generation=1575561865951878\&alt=media)

Рассказывать про то как работает promise я не буду, но если у вас есть вопросы, вот мои любимые материалы:

* [Promise](https://learn.javascript.ru/promise) (Кантор)
* [У нас проблемы с промисами](http://habrahabr.ru/post/269465/) (перевод статьи на хабре)

Урок подходит к логическому завершению. Осталось лишь корректно обновлять состояние компонента.

**Задача**: до запроса - сделать isLoading: true, после завершения запроса - обновить `isLoading: false`, и в `news` положить данные, пришедшие с сервера.

(так как решение в пару строк, я сделаю отступ. Очень хочу чтобы вы попробовали сами)

. . . . . .

**Решение**:

*src/App.js*

```jsx
...
componentDidMount() {
  // ставим isLoading true, 
  // то есть запрос за даннмыи начался
  // фактически он начнется в строке с fetch,
  // но на переход от одной строки к другой
  // пройдут миллисекунды
  this.setState({ isLoading: true })
  fetch('http://localhost:3000/data/newsData.json')
    .then(response => {
      return response.json()
    })
    .then(data => {
      // запрос завершился успешно,
      // делаем isLoading: false
      // в news кладем пришедшие данные
      this.setState({ isLoading: false, news: data })
    })
}
...
```

Что интересно, у нас опять все работает. Мы не трогали компонент `<News />`, так как мы вновь просто изменили "источник данных".

Так как запрос за данными происходит на localhost, данные прилетают мнгновенно. Давайте искусственно затормозим этот момент, чтобы увидеть как они "загружаются". Добавим таймаут, конечно же.

*src/App.js*

```jsx
...
componentDidMount() {
  this.setState({ isLoading: true })
  fetch('http://localhost:3000/data/newsData.json')
    .then(response => {
      return response.json()
    })
    .then(data => {
      setTimeout(() => { // добавили задержку
        this.setState({ isLoading: false, news: data })
      }, 3000) // в три секунды
    })
}
...
```

![isloading news true](https://2647972981-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LvLmy8qHKus4tjYXHI4%2F-LvLmyreyxcNGjR6qn1l%2F-LvLnSrKjNaJb3_F1cqk%2Fisloading-news-true.jpg?generation=1575561865951819\&alt=media)

Подождите три секунды и увидите как появится список новостей. Причем, что хочется отметить - опять нам помогает React. Изменился state -> вызвался render. Никаких дополнительных манипуляций ;)

И никакого Redux/Mobx и прочего. Задача решена без "дичи" в виде кучи библиотек, которые здесь не уместны. Поздравляю.

**Итого**: научились выполнять асинхронные запросы и показывать прелоадер.

[Исходный код](https://github.com/maxfarseer/react-course-ru-v2/tree/chp15-async-request).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://max-frontend.gitbook.io/react-course-ru-v2/asinhronnii-zapros.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
