关于useEffect(React Hooks API)的输入数组的困惑

时间:2019-01-09 02:50:21

标签: javascript reactjs react-hooks

诸如useEffectuseMemouseCallback之类的某些React Hooks API具有第二个参数:输入数组:

useEffect(didUpdate, inputs);

如官方文件所述:

@请参阅Conditionally firing an effect

  1.   

    这样,如果其中一项输入发生更改,则始终会重新创建效果。

  2.   

    效果函数中引用的每个值也应出现在输入数组中。

因此我们可以看到,inputs数组承担两项职责。 在大多数情况下,它们运行正常。但有时它们会发生冲突。

例如,我有一个小计数程序,它可以完成两件事:

  1. 单击按钮,然后将计数加1。

  2. 每5秒钟将计数发送到服务器。

代码和框:

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 :(得分:0)

React的回复:

React discussion Github

可能会实施更好的解决方案,但不是现在。

但是生活会继续下去,因此像this这样的解决方法可能会有所帮助:

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

答案 1 :(得分:0)

useRef()可以解决您的问题。我认为这是一个很好的解决方案:code in sandbox

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>
  );
}

编辑

评论后:https://github.com/facebook/react/issues/14543#issuecomment-452996829