Как справиться со случаем, когда ответ Axios приходит после рендеринга компонента? - PullRequest
0 голосов
/ 16 июня 2019

Я создаю приложение для блога в рамках отдыха и реагировать. На домашней странице, под componentDidMount, я отправляю вызов API, используя axios, чтобы получить все статьи и setState из статей для возврата. Как я уже изучал, axios работает над идеей обещания, чтобы код не выполнялся, если API не извлекается для определенного компонента. Пожалуйста, скажите мне, если я ошибаюсь.

Затем я посылаю вызов GET, чтобы узнать имя автора, который написал статью по идентификатору. Хотя я предположил, что аксио работает как обещание. Но это не работает таким образом. Теперь я не знаю, как двигаться дальше.

Вот фрагмент. Итак, в mainBody.js я делаю вызов API как:

class MainBody extends Component {
state = {};

componentDidMount () {
    this.get_all_articles();
  };

get_writer_name (id) {
    let authstr = 'Bearer ' + window.localStorage.token;
    let writer_url = "http://localhost:8000/api/writer/" + id.toString() + "/";
    axios.get(writer_url, { headers: { Authorization: authstr }})
      .then(response => {
        console.log(response.data['name'])
        return response.data['name'];
      })
      .catch(err => {
        console.log("Got error")
      })
  };

get_all_articles () {
    let authstr = 'Bearer ' + window.localStorage.token;
    axios.get("http://localhost:8000/api/articles/", { headers: { Authorization: authstr }})
      .then(response => {
        this.setState({articles: response.data});
      })
      .catch(err => {
        console.log("Got error")
      })
  }

render () {
return (
{this.state.articles.map((article, key) =>
                          <ArticleView key={article.id} article={article} writer_name={this.get_writer_name(article.created_by)} />
                        )}
)
}
}

В articleview2 я печатаю все данные, присутствующие в каждой из статей, вместе с именем автора.

Мой класс просмотра статьи:

class ArticleView extends Component {
state = {article: this.props.article};
componentDidMount() {
console.log(this.props.writer_name;
}
render () {
return (
<React.Fragment>
<h2>{article.title}</h2>
<p>{article.body}</p>
<span>{this.props.writer_name}</span>
</React.Fragment>
)
}
}

Если вы внимательно посмотрите, я написал два оператора console.log, чтобы получить имена писателей. В зависимости от порядка сначала запускается консольный журнал, представленный в классе articleview, который не определен, а затем после выборки данных из вызова API запускается консольный журнал, который возвращает правильное имя записывающего.

Я хотел знать, где ошибка? Кроме того, как я заметил, слишком много вызовов API делается для многократного получения имени автора для всех перечисленных статей. Каковы лучшие отраслевые практики для этих случаев?

Ответы [ 2 ]

1 голос
/ 16 июня 2019

Поскольку ваши вызовы API имеют много общего, вы должны сначала настроить экземпляр axios, который повторно использует эти общие функции:

const api = axios.create({
  baseURL: 'http://localhost:8000/api/',
  headers: { Authorization: `Bearer ${localStorage.token}` }
});

Теперь, поскольку вашему MainBody необходимо извлекать ресурсы из API асинхронно, будет короткий период, когда данные еще не будут доступны. Есть два способа справиться с этим. Либо MainBody может отвечать за все вызовы, либо он может отвечать только за вызов, чтобы получить все статьи, тогда каждый из ArticleView компонентов может отвечать за получение имени автора. Я продемонстрирую первый подход ниже:

class MainBody extends Component {
  state = { articles: null, error: null, isLoading: true };

  async componentDidMount () {
    try {
      const response = await api.get('articles/');
      const articles = await Promise.all(
        response.data.map(async article => {
          const response = await api.get(`writer/${article.created_by}/`);
          return { ...article, writer_name: response.data.name };
        })
      );

      this.setState({ articles, isLoading: false });
    } catch (error) {
      this.setState({ error, isLoading: false });
    }
  }

  render () {
    const { articles, error, isLoading } = this.state;

    return isLoading ? 'Loading...' : error
      ? `Error ${error.message}`
      : articles.map(article => (
        <ArticleView
          key={article.id}
          article={article}
          writer_name={article.writer_name}
        />
      )
    );
  }
}
0 голосов
/ 16 июня 2019

Я хочу знать, где находится ошибка.

Когда вы пишете this.state.articles.map(), это означает, что вы используете карту свойств статей Array, которые могут быть неопределенными до получения данных, что приведет к ошибке Cannot read property map of undefined.

Решение

Теперь, поскольку запрос API является асинхронным, это означает, что метод рендеринга не будет ожидать поступления данных. Итак, вы можете использовать переменную loader в состоянии и установить ее в значение true, пока выполняется запрос, и когда получен ответ, задайте значение false и покажите загрузчик в render, когда this.state. loader имеет значение true, и показывает статьи, когда он равен false.
Или вы можете инициализировать this.state.articles пустым массивом, который не вызовет ошибку.

Кроме того, как я заметил, слишком много вызовов API делается для многократного получения имени автора для всех перечисленных статей. Каковы лучшие отраслевые практики для этих случаев?

Чрезвычайно плохо делать запрос API в цикле. Даже меня ругали за это, когда я делал это в своей компании.

Решение

Вы должны указать своему бэкэнд-инженеру предоставить вам фильтр для включения имени автора в каждый объект статьи. Мы используем Loopback на нашем бэкэнде, который обеспечивает фильтр для включения связанной модели в каждый объект внутри.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...