Выполнение вызова Axios в функции forEach для изменения объекта не распознается при рендеринге, несмотря на то, что он находится в состоянии

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

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

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

Я удалил вызов API из цикла forEach и вместо этого просто установил статическое значение в цикле, и информация отображается правильно, как и ожидалось. Поэтому я уверен, что проблема в том, что вызов API находится в цикле. Я не могу понять, почему. Журнал показывает, что массив заполнен, поэтому кажется бессмысленным, что рендеринг не может найти данные.

// The API call

loadUserAndTrailers = () => {
        let trailers = []; 
        axios.get("http://localhost:5000/api/trailers")
        .then (res => {
            trailers = res.data;
        })
        .then (() => {          
            trailers.forEach(trailer => {

 axios.get(`http://localhost:5000/api/votes/user/${this.state.user.id}/${trailer.TrailerID}`)
                .then (res => {
                    const vote = res.data[0].Vote;
                    trailer.vote = vote;
                })
            })
        })
        .then (() => {
            this.setState({trailers: trailers});
        })
}

// The child component

const TrailerList = props => {

    console.info(props)

    const trailers = props.trailers.map(trailer => {
        return <div>{trailer.vote}</div>
    });

    return <div>{trailers}</div>;
};

// The return in the parent component

return (
  <div className = "container">
    <div className = "content">
      <h1>Trailers</h1>
      <div className = "trailers">
      <TrailerList trailers = {this.state.trailers}></TrailerList>
    </div>
  </div>
</div>
);

console.info(props) показывает мне завершенный массив с массивом объектов с этим существующим значением ключа {vote: 1}, но ничего не отображает. Как это может быть? Я уже два дня бьюсь головой о стену, но, по общему признанию, я новичок в API и промисах. У меня сложилось впечатление, что обещание должно убедиться, что вызов завершен, прежде чем переходить к следующему шагу, и мое правильно зарегистрированное состояние, похоже, подразумевает, что этот аспект кода работает должным образом.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
3
0
2 223
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

The console.info(props) shows me a completed array with an array of objects with this key value existing { vote: 1 } yet it renders nothing. How can this be?

Сроки. Вы запускаете цикл, делаете вызовы, затем не жди, пока они не закончатся; ваш setState столкнется с незаполненными трейлерами, а через некоторое время axios начнет их заполнять.

Что нужно знать о console.info (по крайней мере, в Chrome), так это то, что он также асинхронен при рендеринге объектов. Любые строки, которые вы регистрируете, будут зарегистрированы как есть; но для отрисовки объектов требуется время, и к тому времени, когда браузер доберется до этого, он может отрисовывать объект таким, какой он есть во время рисунок, а не во время Ведение журнала. Так что вы видите не то, что setState видели. См. Является ли консоль Chrome JavaScript ленивой для оценки массивов?. Используйте JSON.stringify или выберите примитивные значения, чтобы записать то, что есть на самом деле.

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

1) Прямо сейчас обещание axios в каждой итерации цикла (с его цепочкой then) равно отброшенный — на нем ничего не ждет. Если мы вернем его и превратим forEach в map, мы получим массив обещаний.

2) Чтобы дождаться всех промисов в массиве, используйте Promise.all.

Так...

.then (() =>
  Promise.all(trailers.map(trailer =>
    axios.get(`${baseUrl}/${this.state.user.id}/${trailer.TrailerID}`)
    .then(res => {
      const vote = res.data[0].Vote;
      trailer.vote = vote;
    })
  ))
})

Кажется, это работает. Это все еще похоже на черную магию, и у меня возникла очень странная проблема. В вашем Promise.all(trailers.map(trailer => вы не используете фигурную скобку после. Сила привычки заставила меня использовать их при написании своего. Это не сработало. Однажды я их удалил. Все, казалось, работало. Почему фигурная скобка может нарушить функциональность обещания? Кроме того, просто чтобы уточнить, обещание отбрасывается, если внутренняя функция не возвращает значение?

Dale Spiteri 11.07.2019 01:25

Сначала последний вопрос: в цепочке обещаний у вас должна быть цепочка. Если вы ее где-то разорвете, вы не будете ждать конечного результата, вы будете ждать только того момента, когда цепь оборвется. Это имеет смысл, верно? Что касается фигурок: стрелочные функции имеют две формы: params => expression или params => { statements }. Первый неявно возвращает. Например. (a, b) => a + b эквивалентно (a, b) => { return a + b; }.

Amadan 11.07.2019 02:37

@ Амадан Да, кажется, я понимаю. Будет ли тогда мое первое обещание неверным? .then ( res => { trailers = res.data; } ). Я понимаю, почему это не сломает код, потому что он не асинхронный, но я действительно должен вернуться, чтобы правильно связать их, верно? Могу ли я просто добавить пустой возврат в конце этого процесса? Вот так: .then ( res => { trailers = res.data; return; } ), или, точнее, теперь, когда вы это объяснили, вот так: .then ( res => trailers = res.data; ). Я просто никогда не думал возвращать значения, когда возврат не добавляет какой-либо заметной функциональности.

Dale Spiteri 11.07.2019 17:38

Каждый раз, когда у вас нет возврата из функции, неявно возвращается undefined (за исключением стрелки в стиле выражения), точно так же, как когда вы просто делаете голый return без значения. Таким образом, два ваших фрагмента в последнем комментарии по сути идентичны. Они оба верны; и под «цепочкой» я имел в виду цепочки обещаний; это не повлияет на них.

Amadan 12.07.2019 02:40

Другие вопросы по теме