Выполнение нескольких вызовов API и обновление локального состояния в React

avatar
yourAverageDeveloper
9 августа 2021 в 06:57
385
1
0

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

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

Настоящая кодовая база довольно сложна, поэтому я реконструировал ее базовую версию здесь. Вызов API просто добавит свойство email к существующему объекту, а затем обновит локальное состояние. Я смоделировал вызов API, используя setTimeout, и функция вызова API срабатывает при монтировании компонента. Когда цикл завершится и все вызовы setState будут выполнены, в моем браузере появится только последний объект в переменной состояния, а не все объекты.

Поэтому я ожидаю ответа вида

[
  {
    id: 1,
    name: 'Adam',
    email: 'adam@gmail.com'
  },
  {
    id: 2,
    name: 'Michael',
    email: 'michael@gmail.com'
  },
  {
    id: 3,
    name: 'Joseph',
    email: 'joseph@gmail.com'
  },
  {
    id: 4,
    name: 'Noah',
    email: 'noah@gmail.com'
  }
]

Но вместо этого я получаю

[
  {
    id: 4,
    name: 'Noah',
    email: 'noah@gmail.com'
  }
]

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

Итак, вот проблема, мне нужно сделать вызовы API для всех записей в массиве сразу, и как только я получу ответ, мне нужно обновить состояние. В некоторых ответах, которые я видел здесь, говорится об использовании Promise.all, но я считаю, что это будет ждать, пока все вызовы API не будут разрешены, но это повлияет на производительность моего приложения. Здесь также нельзя использовать useEffect, потому что я не хочу ждать завершения одного вызова API, а затем вызывать следующий. Но, может быть, я неправильно понимаю useEffect?

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

Источник
CVerica
9 августа 2021 в 07:02
1

Можете ли вы делать вызовы API и обновлять состояние только после завершения всех вызовов?

Phil
9 августа 2021 в 07:11
0

Пожалуйста, включите в свой вопрос минимальный воспроизводимый пример (не только на JSFiddle).

yourAverageDeveloper
9 августа 2021 в 10:06
0

@CVerica К сожалению, я не могу :( Не могу дождаться, пока все вызовы API вернут данные, так как со временем массив будет расти намного больше, и это действительно повлияет на производительность приложения с точки зрения того, как его воспринимают пользователи.

Ответы (1)

avatar
marekful
9 августа 2021 в 07:06
0

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

  React.useEffect(() => {
    fetchUsers();
  }, [users]);

Исходное состояние users — это пустой массив, как определено здесь

const [users, setUsers] = React.useState([]);

Вы можете прочитать в документации

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

Пожалуйста, прочтите документы.

yourAverageDeveloper
9 августа 2021 в 10:04
0

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

marekful
9 августа 2021 в 14:53
0

Да, это так. Эта конструкция изначально ошибочна. Я просто указал на тот факт, что любая переменная, не переданная в качестве зависимости useEffec(), будет каждый раз использовать свое начальное состояние. Вы определенно не должны переназначать весь объект users каждый раз, как вы делали это в своей скрипке. А как насчет Array.push()?

yourAverageDeveloper
9 августа 2021 в 19:58
0

Array.push отлично работал бы, если бы я мог перерендерить компонент React после него. Необходимо использовать функцию установки состояния, чтобы вызвать повторную визуализацию, которая обновит пользовательский интерфейс ☹️