получить последнее значение состояния после перехвата setState в блоке asyn c в реакции - PullRequest
2 голосов
/ 20 февраля 2020

У меня есть раскрывающийся список, который при изменениях вызывает API для извлечения данных с использованием useEffect hook:

const [selectedComplianceFilter, setComplianceFilter] = useState("all");

useEffect(() => {
  if (selectedComplianceFilter === "all") {
    fetchFullReport();
  } else {
    fetchReportByCompliance(selectedComplianceFilter);
  }
}, [selectedComplianceFilter]);

<select
  name="compliance"
  className="custom-select w-auto"
  onChange={e => setComplianceFilter(e.target.value)}
>
  <option value="all">All</option>
  {compliances.map((compliance, i) => (
    <option key={i} value={compliance}>
      {compliance}
    </option>
  ))}
</select>

У меня есть еще один useEffect хук, который вызывает другой API, который возвращает статус сканирования

useEffect(() => {
  fetchCompliances();

  let intervalId;

  getScanStatus(0);
  fetchFullReport();

  intervalId = setInterval(() => {
    getScanStatus(intervalId);
  }, 10000);

  return () => {
    componentDidUnmount.current = true;
    clearInterval(intervalId);
  };
}, []);

Это функция для извлечения статуса сканирования:

const [scanStatus, setScanStatus] = useState("");
const [lastUpdated, setLastUpdated] = useState();
const [reports, setReports] = useState([]);

const getScanStatus = async intervalId => {
  const resp = await API.get(SCAN_STATUS_URL);

  if (resp && resp.status) {
    const { scanStatus = "", lastModifiedDate = "" } = resp.data || {};

    const isValidDate = moment(lastModifiedDate).isValid();

    if (!componentDidUnmount.current) {
      setScanStatus(scanStatus);
      isValidDate && setLastUpdated(getFormattedDateTime(lastModifiedDate));
      if (scanStatus === "COMPLETED") {
        clearInterval(intervalId);

        console.log("selectedComplianceFilter", selectedComplianceFilter); // <== returns old state i.e., 'all' but should be latest selected option.

        if (selectedComplianceFilter === "all") {
          fetchFullReport();
        } else {
          fetchReportByCompliance();
        }
      }
    }
  }
};

Теперь, когда происходит загрузка страницы, fetchFullReport(); отображает таблицу. Теперь, когда раскрывающийся список изменяется, вызывается fetchReportByCompliance(); для фильтрации таблицы, и таблица обновляется с меньшим количеством строк.

Между тем getScanStatus вызывается через каждые 10 секунд, и как только API возвращает status = 'COMPLETED', интервал останавливается, и таблица обновляется новыми данными (fetchFullReport();) и всеми строками таблицы. вернулись, что является странным поведением, так как выпадающий фильтр все еще имеет выбранный параметр, отличный от ALL Поэтому, когда новые данные поступают, как только сканирование завершено для обновления таблицы, я добавил условие if для обновления таблицы на основе значения selectedComplianceFilter.

Но когда функция getScanStatus api возвращает status = 'COMPLETED', значение selectedComplianceFilter по-прежнему равно 'all', но в раскрывающемся меню выбрана другая опция. Я предполагаю, что это из-за блока async await, который все еще содержит предыдущее значение состояния selectedComplianceFilter.

Как получить последнее значение состояния selectedComplianceFilter, когда getScanStatus выполняется в будущем, поскольку оно асинхронно?

1 Ответ

1 голос
/ 20 февраля 2020

Как насчет упаковки вашего кода в блок useEffect, который срабатывает при изменении scanStatus? Например:

useEffect(() => {
    if (scanStatus === "COMPLETED") {
        if (selectedComplianceFilter === "all") {
            fetchFullReport();
        } else {
            fetchReportByCompliance();
        }
   }
}, [scanStatus]);
...