Реагировать на useEffect, выполняя последнюю функцию

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

const loadCategories = () => {
    getCategories().then((c) => setValues({ ...values, categories: c.data }));
}

const loadStores = () => {
    getStores().then((c) => setValues({ ...values, stores: c.data }));
}

useEffect(() => {
  loadStores();
  loadCategories();
}, []);

Обе функции устанавливают значения выпадающих элементов.

Проблема в том, что обе функции выполняются, но в пользовательском интерфейсе отображается только логика loadCategories() функций. Как заставить логику обеих функций отражаться в пользовательском интерфейсе?

Находится ли функция useEffect внутри loadStores? Похоже, у вас может быть бесконечная рекурсия?

Sam 13.12.2020 20:24

Нет, это за пределами loadStores. 2 функции load находятся за пределами useEffect

bigbounty 13.12.2020 20:29

Единственная причина, по которой я могу сделать вывод, заключается в том, что loadCategories() переопределяет loadStores(), но это не так, потому что первое заданное состояние в categories и второе заданное состояние в stores. Можете ли вы попробовать запустить только первую функцию loadStories() и проверить, обновляет ли пользовательский интерфейс только эта функция. Может проблема в нем, или при нанесении на UI.

Predrag Davidovic 13.12.2020 20:36

@PredragDavidovic Когда я использую только одну функцию внутри useEffect, она работает нормально, но когда я использую 2 функции одну за другой, последняя функция будет отображаться в пользовательском интерфейсе, даже если обе функции запущены

bigbounty 13.12.2020 20:39
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
4
419
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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

useEffect(() => {
    Promise.all([getCategories(), getStores()]).then(([categories, stores]) => {
      setValues({categories, stores})
    });
}, []);

Проблема с тем, что у вас было раньше (как вы поняли), заключается в том, что values всегда является значением в точке, в которой useState запускался на этом рендере.

Если вы действительно хотите делать обновления отдельно, вы можете заглянуть в useReducer: https://reactjs.org/docs/hooks-reference.html#usereducer

Обещание и useEffect могут быть сложными, поскольку компонент может отключиться до того, как обещание будет полностью заполнено.

Вот решение, которое работает довольно хорошо:

useEffect(() => {
  let isRunning = true;
  Promise.all([ 
    getCategories(),
    getStores()
  ]).then(([stores, categories]) => {
    // Stop if component was unmounted:
    if (!isRunning) { return }
    // Do anything you like with your lazy load values:
    console.info(stores, categories)
  });
  return () => {
    isRunning = false;
  }
}, []);

Вы лжете React о зависимостях. Обе ваши функции зависят от переменной состояния values. Вот почему это также не работает: когда хуки запускаются, values закрывается, затем, когда setValues запускается, values изменяется (устанавливается на новый объект), однако внутри замыкания он все еще ссылается на старые значения.

Вы можете легко решить эту проблему, передав обратный вызов setValues, таким образом, у вас не будет зависимости от внешнего мира, а обновление значений будет атомарным:

 setValues(values => ({ ...values, stores: c.data }));

Это сработало. Я могу принять только один ответ. Следовательно, я проголосовал за

bigbounty 13.12.2020 20:43
Ответ принят как подходящий

сначала лучше добавить эти функции в useEffect или обернуть их в хук useCallback. во-вторых, обе или ваша функция являются обещаниями, поэтому каждая из них может не разрешаться одновременно, и когда вы пытаетесь обновить значения состояния, оно сохранит начальное значение, поэтому ваша первая функция не отражается в пользовательском интерфейсе, вместо этого используйте setState callback, чтобы получить предыдущее состояние следующим образом:

useEffect(() => {
const loadCategories = () => {
    getCategories().then((c) => setValues(prevState=>({ ...prevState, categories: c.data })));
}

const loadStores = () => {
    getStores().then((c) => setValues(prevState=>({ ...prevState, stores: c.data })));
}
  loadStores();
  loadCategories();
}, []);

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