Мне нужно иметь 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()
функций. Как заставить логику обеих функций отражаться в пользовательском интерфейсе?
Нет, это за пределами loadStores
. 2 функции load
находятся за пределами useEffect
Единственная причина, по которой я могу сделать вывод, заключается в том, что loadCategories()
переопределяет loadStores()
, но это не так, потому что первое заданное состояние в categories
и второе заданное состояние в stores
. Можете ли вы попробовать запустить только первую функцию loadStories()
и проверить, обновляет ли пользовательский интерфейс только эта функция. Может проблема в нем, или при нанесении на UI.
@PredragDavidovic Когда я использую только одну функцию внутри useEffect
, она работает нормально, но когда я использую 2 функции одну за другой, последняя функция будет отображаться в пользовательском интерфейсе, даже если обе функции запущены
Подождите, пока оба обещания разрешатся, а затем используйте данные из обоих для обновления своего состояния, комбинируя их так, как вы считаете нужным.
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 }));
Это сработало. Я могу принять только один ответ. Следовательно, я проголосовал за
сначала лучше добавить эти функции в 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();
}, []);
Находится ли функция
useEffect
внутриloadStores
? Похоже, у вас может быть бесконечная рекурсия?