首页 > 解决方案 > 为什么添加输入事件侦听器会破坏反应中的托管输入?

问题描述

我正在尝试编写一个钩子,它接受一个(参考 a)输入元素并监听它的更新。由于它是与渲染分离的,因此在挂钩中,我不能onInput直接在元素上使用普通道具。

在事件侦听器中更新不相关的状态(使用useState钩子)时,输入元素会中断,您无法输入它,而是插入符号/焦点更改。

为什么会addEventListener('input', handleInput)onInput={handleInput}这种方式做出不同的反应,以及解决此问题的最佳方法是什么?

这是重现该问题的片段:

const Hello = (props) => {
  const inputRef = React.useRef();
  const [val, setVal] = React.useState("Can't type here");
  const [other, setOther] = React.useState(0);
  
  React.useEffect(() => {
    const inputEl = inputRef.current;
    
    const handleInput = (ev) => {
      console.log('In handleInput');
      setOther(prev => prev + 1)
    };
  
    inputEl.addEventListener('input', handleInput);
    return () => inputEl.removeEventListener('input', handleInput);
  }, []);
  
  return (
    <input 
      ref={inputRef} 
      value={val} 
      onChange={ev => setVal(ev.target.value)}
    />
  );
}

ReactDOM.render(
  <Hello />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>

<div id="react"></div>

标签: reactjsreact-hooks

解决方案


您的非 React 事件正在触发状态更改setOther(prev => prev + 1),这会导致组件在 React 事件发生之前重新渲染。

我通常会建议尽可能不要使用非 React 事件,如果你需要它们的结果代码来影响状态,绝对是这样。

如果您从 vanilla JS 事件处理程序中删除状态更改,则组件应按预期运行:

const Hello = (props) => {
  const inputRef = React.useRef();
  const [val, setVal] = React.useState("Can't type here");
  const [other, setOther] = React.useState(0);
  
  React.useEffect(() => {
    const inputEl = inputRef.current;
    
    const handleInput = (ev) => {
      console.log('In handleInput');
    };
  
    inputEl.addEventListener('input', handleInput);
    return () => inputEl.removeEventListener('input', handleInput);
  }, []);
  
  return (
    <input 
      ref={inputRef} 
      value={val} 
      onChange={ev => setVal(ev.target.value)}
    />
  );
}

ReactDOM.render(
  <Hello />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>

<div id="react"></div>

如果您需要这两个事件,为什么不将它们都附加到 React 事件,就像这样(ReactonChange 监听 input,而不是模糊):

const Hello = (props) => {
  const inputRef = React.useRef();
  const [val, setVal] = React.useState("Can't type here");
  const [other, setOther] = React.useState(0);
  
  return (
    <input 
      ref={inputRef} 
      value={val} 
      onChange={ev => {
        setVal(ev.target.value)
        console.log('In handleInput')
        setOther(prev => prev + 1)
      }}
    />
  );
}

ReactDOM.render(
  <Hello />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>

<div id="react"></div>


推荐阅读