如何使用反应钩子useState添加到对象?

How to add to object using react hook useState?

请看一下这段代码。

function App() {
  useEffect(() => {
    document.addEventListener('keydown', onKeyDown);
  }, []);

  const onKeyDown = e => {
    setKeyMap({ ...keyMap, [e.keyCode]: true });
  };

  const [keyMap, setKeyMap] = useState({});

  // JSON.stringify gives only one key-value pair
  return (
    <div className="App">
      <h1>Hello {JSON.stringify(keyMap)}</h1>
    </div>
  );
}

所以使用 useEffect,我只向 keydown 事件添加了一次事件监听器。这显然类似于 componentDidMount 因为这只发生一次。

在 keydown 事件的事件处理程序中,我试图添加所有被按下为真的键码。

假设我按下 'a',那么我的 keyMap 应该如下所示:

{
    "65": true
}

现在,如果我按 'b',那么我的 keyMap 应该如下所示:

{
    "65": true,
    "66": true
}

但实际情况是这样的:

{
    "66": true
}

为什么我无法改变以前的状态并添加到它?就像我之前将“65”作为键,但是当我按下键盘上的另一个键时,这个“65”键值突​​然消失了。也就是说,在任何给定点,由于某种原因只存在 one 键值对。我希望能够根据需要多次添加到这个对象(我的意思是 keyMap 对象)。 ... 运算符(传播)是否不起作用?

useEffect(() => {
    document.addEventListener('keydown', onKeyDown);
}, []);

由于第二个参数为空数组,因此仅在组件首次呈现时获得一次 运行。 onKeyDown函数在那个时候有一个状态的引用,所以setKeyMap({ ...keyMap, [e.keyCode]: true });中的keyMap指的是一个空对象。每次调用此密钥处理程序时,它都会将状态设置为一个空对象,加上最新的密钥。

相反,您希望它使用最新状态。您有两个主要选项来执行此操作

1) 不要跳过效果。这样,您将始终拥有一个具有最新状态的事件侦听器。记得 return 一个拆卸函数,这样你就没有事件监听器泄漏:

useEffect(() => {
  const onKeyDown = e => {
    setKeyMap({ ...keyMap, [e.keyCode]: true });
  };

  document.addEventListener('keydown', onKeyDown);
  return () => document.removeEventListener('keydown', onKeyDown);
}); // You could also pass [keyMap] as the second param to skip irrelevant changes

2) 或者,使用setKeyMap的功能版本。它会传递给你状态的最新值。

const onKeyDown = e => {
  setKeyMap(prevKeyMap => ({ ...prevKeyMap, [e.keyCode]: true }));
};

您只需像这样将 onKeyDown 函数移动到 useEffect 挂钩中。请注意,我在 useEffect 挂钩的依赖项数组中添加了 keyMap

https://codesandbox.io/s/red-sun-23bmu?fontsize=14