React 2 外部事件无法共享挂钩数据

React 2 ouside events cannot share hook data

我有 2 个外部事件,inputkeydown 用于输入元素,当输入触发时,它将在 React 组件中调用 setInput。当 arrowDownkeydown 事件被触发时,理想情况下输入值应该是 handleInput 中设置的值,但它是空的,我错过了什么吗?

const SearchBox : React.FunctionComponent<ISearchProps> = React.memo((props: ISearchProps) => {
   const [input, setInput] = React.useState({value: '', redirect: false});

    useEffect(() => {
        const container = document.querySelector('.xxx');
        container.addEventListener('input', handleInput);
        container.addEventListener('keydown', handleKeyboardEvent);
    }, []);

    const handleInput = (e) => {
        setInput({value: e.currentTarget.value, redirect: false});
    }

    const handleKeyboardEvent = (e) => {
        switch (e.key) {
            case 'ArrowDown':
                console.log(input);
                setActiveSuggestionIndex(activeSuggestionIndex === suggestions.length - 1 ? 0 : activeSuggestionIndex + 1);
                e.preventDefault();
                break;
            default:
                break;
        }
    }
}

问题

这是回调函数中陈旧状态封装的问题。 { value: '', redirect: false } 的初始 input 状态值在 handleKeyboardEvent 的副本中从初始渲染周期关闭。永远不会更新。

解决方案

使用 React ref 和 useEffect 缓存值的副本

使用 React ref 和额外的 useEffect 钩子来缓存要在回调中异步访问的值。

const [input, setInput] = React.useState({ value: '', redirect: false });
const inputRef = React.useRef(input); // <-- state cache

useEffect(() => {
  const container = document.querySelector('.xxx');
  container.addEventListener('input', handleInput);
  container.addEventListener('keydown', handleKeyboardEvent);

  return () => {
    container.removeEventListener('input', handleInput);
    container.removeEventListener('keydown', handleKeyboardEvent);
  };
}, []);

useEffect(() => {
  inputRef.current = input; // <-- update state cache value
}, [input]);

const handleInput = (e) => {
  setInput({ value: e.currentTarget.value, redirect: false });
};

const handleKeyboardEvent = (e) => {
  switch (e.key) {
    case 'ArrowDown':
      console.log(inputRef); // <-- read current state cache value

      setActiveSuggestionIndex(activeSuggestionIndex => 
        activeSuggestionIndex === suggestions.length - 1
          ? 0
          : activeSuggestionIndex + 1
      );
      e.preventDefault();
      break;
    default:
      break;
  }
};

useEffect与依赖和清理功能一起使用

由于您还应该已经从 useEffect 返回清理函数以在卸载时删除事件侦听器,因此将 input 状态添加到依赖项数组( 和任何linter 可能会抱怨其他缺少的依赖项 ),因此当 input 状态更新时,当前状态值会重新包含在回调范围中。

useEffect(() => {
  const handleInput = (e) => {
    setInput({ value: e.currentTarget.value, redirect: false });
  };

  const handleKeyboardEvent = (e) => {
    switch (e.key) {
      case 'ArrowDown':
        console.log(input);

        setActiveSuggestionIndex(activeSuggestionIndex => 
          activeSuggestionIndex === suggestions.length - 1
            ? 0
            : activeSuggestionIndex + 1
        );
        e.preventDefault();
        break;
      default:
        break;
    }
  };

  const container = document.querySelector('.xxx');

  container.addEventListener('input', handleInput);
  container.addEventListener('keydown', handleKeyboardEvent);

  return () => {
    container.removeEventListener('input', handleInput);
    container.removeEventListener('keydown', handleKeyboardEvent);
  };
}, [input]);