Путаница с массивом входных данных useEffect (React Hooks API) - PullRequest
0 голосов
/ 09 января 2019

Некоторые API React Hooks, такие как useEffect, useMemo, useCallback, имеют второй параметр: массив входных данных:

useEffect(didUpdate, inputs);

Как сказано в официальном документе:

@ см. Условное срабатывание эффекта

  1. Таким образом, эффект всегда воссоздается при изменении одного из его входов.

  2. каждое значение, указанное в функции эффекта, также должно появляться в массиве input.

Итак, как мы видим, массив inputs принимает две обязанности. В большинстве ситуаций они работают правильно. Но иногда они конфликтуют.

Например, у меня есть небольшая программа для подсчета, она делает две вещи:

  1. Нажмите кнопку и количество плюс 1.

  2. Отправлять счет на сервер каждые 5 секунд.

Codesandbox:

https://codesandbox.io/s/k0m1mq9v

Или посмотрите код здесь:

import { useState, useEffect, useCallback } from 'react';

function xhr(count) {
  console.log(`Sending "${count}" to my server.`);
  // TODO send count to my server by XMLHttpRequest
}

function add1(n) {
  return n + 1;
}

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  // Handle click to increase count by 1
  const handleClick = useCallback(
    () => setCount(add1),
    [],
  );

  // Send count to server every 5 seconds
  useEffect(() => {
    const intervalId = setInterval(() => xhr(count), 5000);
    return () => clearInterval(intervalId);
  }, []);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={handleClick}>
        Click me
      </button>
    </div>
  );
}

export default Example;

Когда я запускаю этот код, я всегда отправляю count = 0 на мой сервер, потому что я не передал count useEffect.

Но если я передам count в useEffect, мой setInterval будет очищен и весь обратный вызов будет воссоздан каждый раз, когда я нажимаю кнопку.

Я думаю, может быть, есть еще одна парадигма для достижения моей цели, о которой я даже не задумываюсь. Если нет, то это конфликт массива inputs.

Ответы [ 2 ]

0 голосов
/ 10 января 2019

useRef() может решить вашу проблему. Я думаю, что это элегантное решение: код в песочнице

function App() {
  const [count, setCount] = useState(0);

  // ***** Initialize countRef.current with count
  const countRef = useRef(count);

  const handleClick = useCallback(() => setCount(add1), []);

  // ***** Set countRef.current to current count
  // after comment https://github.com/facebook/react/issues/14543#issuecomment-452996829
  useEffect(() => (countRef.current = count));

  useEffect(() => {
    // ***** countRef.current is xhr function argument
    const intervalId = setInterval(() => xhr(countRef.current), 5000);
    return () => clearInterval(intervalId);
  }, []);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={handleClick}>Click me</button>
    </div>
  );
}

EDIT

После комментария: https://github.com/facebook/react/issues/14543#issuecomment-452996829

0 голосов
/ 10 января 2019

Ответ от React :

Реагирует на обсуждение Github

Лучшее решение может быть реализовано, но не сейчас.

Но жизнь будет продолжаться, поэтому обходной путь, подобный , может помочь:

const [count, setCount] = useState(0);
const countRef = useRef(count);
useEffect(() => {
  countRef.current = count
}, [count]);
...