Метод не видит измененный стейт, а лишь его старое состояние
import React, { useCallback, useEffect, useState, useRef } from "react";
import queryString from "query-string";
import { useLocation } from "react-router";
import { getBaseAreasForReports } from "./loaders";
export const TestUseEffect = () => {
const [pages, setPages] = useState([]);
const location = useLocation();
const prevLocation = usePrevious(location);
const areas2Pages = (areas) => {
const floors = {};
areas.forEach((area) => {
if (floors[area.floor_id]) floors[area.floor_id].push(area);
else floors[area.floor_id] = [area];
});
return Object.entries(floors).map((entry) => entry[1]);
};
const batchLoading = useCallback(() => {
setTimeout(() => console.log('batchLoading', pages), 1000)
},[pages])
useEffect(() => {
const locationChanged = location !== prevLocation
console.log('location changed', locationChanged)
if (locationChanged) {
const query = queryString.parse(location.search)
getBaseAreasForReports(
query.continent_id,
query.country_id,
query.city_id,
query.building_id,
query.floor_id,
(areas) => {
const floors = areas2Pages(areas);
setPages(floors);
batchLoading();
}
);
}
}, [location, batchLoading, prevLocation]);
useEffect(()=>{
console.log('pages changed', pages)
}, [pages])
return (
<div>
Test
{pages.map((page) => (
<div key={`page_floor_id_${page[0].floor_id}`}>
{page.map((area) => (
<div key={`floor_area_id${area.floor_area_id}`}>
{area.floor_area_id}
</div>
))}
</div>
))}
</div>
);
};
// Hook
function usePrevious(value) {
// The ref object is a generic container whose current property is mutable ...
// ... and can hold any value, similar to an instance property on a class
const ref = useRef();
// Store current value in ref
useEffect(() => {
ref.current = value;
}, [value]); // Only re-run if value changes
// Return previous value (happens before update in useEffect above)
return ref.current;
}
Задача такая: при смене значений в адресной строке (там содержится id) на сервер делается запрос и получает целый список других id (не буду вдаваться в подробности). Далее после успешного получения список парсится (метод areas2Pages) и записывается в стейт pages.
Далее другой специальный метод (это должен быть batchLoading) должен запуститься, взять 5 id из списка и пометить их как загружаемые в данный момент и соответственно сделать сразу 5 запросов на сервер. Когда один из запросов получит ответ, то он должен снова вызвать этот же метод, и пометить загруженный id как "загруженный" и найти следующий id который еще не загружен. И т.д. пока не пройдет по списку.
Задача, вроде не сложная. Я взял useEffect и там вызываю запрос к серверу каждый раз, как только изменится адресная строка. Как только ответ получен, я парсю через areas2Pages данные и сохраняю в pages, а далее вызываю тот метод batchLoading, чтобы он начал проход по списку. И бы мог ему сразу передать распарсенный floors, который по сути и сохранялся в стейт только-что. Но, замысел в том, чтобы метод batchLoading сам брал из стейта данные. Но проблема в том, что он не видит измененный стейт. Да, сначала я первым делом подумал - скорее всего он не видит, потому-что стейт еще не успел обновиться. Тогда я сделал таймаут в 0 секунд. Это срабатывало раньше в таких ситуациях. Но тут не сработало. Тогда я задал больше время, но тоже не сработало.
Я даже попробовал сделать таймаут прямо после сохранения стейта и распечатать pages прямо там.
useEffect(() => {
const locationChanged = location !== prevLocation;
console.log("location changed", locationChanged);
if (locationChanged) {
const query = queryString.parse(location.search);
getBaseAreasForReports(
query.continent_id,
query.country_id,
query.city_id,
query.building_id,
query.floor_id,
(areas) => {
const floors = areas2Pages(areas);
setPages(floors);
setTimeout(()=>console.log('pages', pages), 0)
//batchLoading();
}
);
}
}, [location, pages, prevLocation]);
И даже передал в зависимости pages. Но и это не сработало. Я создал еще один useEffect для pages, чтобы распечатать их при любых изменениях в этом стейте. И они срабатывают до того как распечатываются pages по таймауту - т.е. теоретически к тому времени как таймаут сработал, этот стейт уже поменялся, но тем не менее в логе пустой массив.
Вот лог:
Подскажите решение, как заставить метод batchLoading видеть обновленный стейт pages?
Ответы (1 шт):
Может просто перенести вызов?
useEffect(() => {
const locationChanged = location !== prevLocation
console.log('location changed', locationChanged)
if (locationChanged) {
const query = queryString.parse(location.search)
getBaseAreasForReports(
query.continent_id,
query.country_id,
query.city_id,
query.building_id,
query.floor_id,
(areas) => {
const floors = areas2Pages(areas);
setPages(floors);
batchLoading();
}
);
}
}, [location, batchLoading, prevLocation]);
useEffect(()=>{
//почему не перенести сюда? pages обновится и ваша функция сработает и без
//useCallback
batchLoading();
console.log('pages changed', pages)
}, [pages])
