React Hooks:旧状态值在关闭中持续存在

React Hooks: old state value persisting in closure

好吧,我意识到这将是一个超长的例子,但我对此感到非常困惑,所以我想我会与大家分享并尝试找到解决方案。

所以我正在尝试制作一个 "tag input" 组件,您可以在其中键入内容,并且每当您键入 space 时,它都会将该字符串附加到您传入的列表中。为了删除一个在 "tags" 中,您清除了内容可编辑区域,回击 space 一次至 "prep" 列表中要删除的最后一个标签,然后再次确认删除。它在上下文中是有意义的,但为了这个例子,我写了一个精简版本。我有以下代码和框: https://codesandbox.io/s/8q0q3qw60

现在,这是我不明白的部分。

除了实际删除标签外,一切似乎都完全按预期工作。出于某种原因,我可以适当地 "prep" 删除最后一个标签,但是当我再次单击返回 space 确认时,由于某种原因 prepTagForRemoval 的状态(来自钩子)作为内容可编辑区域的 keyDown 回调的闭包永远不会改变。它始终为 false,但 仅在回调 内!这导致标签在确认删除后永远不会被正确删除。

为了重现这个...

  1. 打开代码沙箱link
  2. 单击红色框(内容可编辑 div)
  3. 键入 "hello "(不带引号,所以你好后跟 space
  4. hello 将向上移动到上面写着 "Tags:"
  5. 的行
  6. 点击返回space一次
  7. 准备删除现在为真,"hello" 以红色突出显示
  8. 再次点击返回space

目前,在这一点上,它应该刚刚从"Tags:"行中删除了"hello",然而实际的行为是它只是设置了prepForRemoval为 false,并再次将 hello 变黑,而不从 "Tags:"

中删除 "hello"

抱歉,如果这让您感到困惑,我很乐意尝试澄清更多。我真的想让这个示例正常工作并在发出第二个删除时删除最后一个标记(或至少调用 onRemove)。希望有人能伸出援手!

这是 react-contenteditable 中的错误。下面是它的 shouldComponentUpdate 方法。它不检查对 onKeyDown 的更改,并且由于退格键不会导致值发生任何更改,因此它不会重新呈现,因此它使用的是 handleKeyDown 函数的陈旧版本.

  shouldComponentUpdate(nextProps: Props): boolean {
    const { props } = this;
    const el = this.getEl();

    // We need not rerender if the change of props simply reflects the user's edits.
    // Rerendering in this case would make the cursor/caret jump

    // Rerender if there is no element yet... (somehow?)
    if (!el) return true;

    // ...or if html really changed... (programmatically, not by user edit)
    if (
      normalizeHtml(nextProps.html) !== normalizeHtml(el.innerHTML)
    ) {
      return true;
    }

    // Handle additional properties
    return props.disabled !== nextProps.disabled ||
      props.tagName !== nextProps.tagName ||
      props.className !== nextProps.className ||
      props.innerRef !== nextProps.innerRef ||
      !deepEqual(props.style, nextProps.style);
  }

这里使用的是 react-contenteditable 的固定副本:https://codesandbox.io/s/o41yjr3r3q

我将 shouldComponentUpdate 的最后一部分更改为添加 props.onKeyDown !== nextProps.onKeyDown:

return (
  props.disabled !== nextProps.disabled ||
  props.tagName !== nextProps.tagName ||
  props.className !== nextProps.className ||
  props.innerRef !== nextProps.innerRef ||
  props.onKeyDown !== nextProps.onKeyDown ||
  !deepEqual(props.style, nextProps.style)
);