Как я могу использовать эффект один раз, но все же использовать состояние с React Hooks? - PullRequest
3 голосов
/ 07 мая 2019

Я пытаюсь установить интервал только при монтировании / размонтировании, но разрешить обратному вызову иметь одну переменную из состояния.

У меня есть такой код:

const [cachedData, setCachedData] = useState(false);

async function refreshData() {
  const data = await axios('http://www.example.com/');
  setCachedData(data);
}

useEffect(() => {
  let interval;
  async function fetch() {
    await refreshData();
    interval = setInterval(refreshData,5000);
    console.log('set interval to', interval)
  }

  fetch();

  return () => {
    console.log('clearing interval', interval);
    clearInterval(interval);
  }
}, []);

Я сталкиваюсь с ловушкой-22. Второй аргумент useEffect говорит о том, на какие переменные следует обратить внимание. Я читал, что создание пустого массива делает это только монтированием / размонтированием, а не обновлениями состояния Проблема, которую я обнаружил, заключается в том, что при этом refreshData не имеет доступа к cachedData, поэтому я не могу знать, что у меня есть действительные данные (чтобы избежать запроса XHR в течение некоторого времени). Если я передам cachedData во второй аргумент useEffect, он будет иметь переменную, но выполнит больше, чем должен. Не уверен, что можно обойти это.

Я должен отметить, что если я передам cachedData во второй аргумент и console.log с помощью очистки и установки интервала, моя консоль выведет что-то вроде этого:

clearing interval undefined
set interval to 5
set interval to 7

Так что, похоже, запускается размонтирование, а затем эффект дважды повторяется, не очищаясь снова. Это вызывает двойной аксиозный вызов.

1 Ответ

1 голос
/ 08 мая 2019

Вы можете использовать ссылку, чтобы получить текущую cachedData что-то вроде следующего:

const [cachedData, setCachedData] = useState(false);
const cachedDataRef = useRef(cachedData);

useEffect(() => {
  // Update ref in effect so that render has no side effects.
  cachedDataRef.current = cachedData;
}, [cachedData]);

useEffect(() => {

  async function refreshData() {
    if (cachedDataRef.current) {
      // do something different
    } else {
      const data = await axios('http://www.example.com/');
      setCachedData(data);
    }
  }
  let interval;
  async function fetch() {
    await refreshData();
    interval = setInterval(refreshData,5000);
    console.log('set interval to', interval)
  }

  fetch();

  return () => {
    console.log('clearing interval', interval);
    clearInterval(interval);
  }
}, []);
...