【问题标题】:React 2 ouside events cannot share hook dataReact 2 外面的事件不能共享钩子数据
【发布时间】:2022-01-10 03:55:54
【问题描述】:

我有 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;
        }
    }
}

【问题讨论】:

  • 你调用事件的方式不是很 ReactJS 方式,但尝试切换到这个:setInput({...input, value: e.currentTarget.value, redirect: false});

标签: javascript reactjs react-hooks dom-events


【解决方案1】:

问题

这是回调函数中状态过时的问题。 { value: '', redirect: false } 的初始 input 状态值在来自初始渲染周期的 handleKeyboardEvent 的副本中被关闭。它永远不会更新。

解决方案

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

使用 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]);

【讨论】:

  • 谢谢,可以了,但是改成useRef后好像没有重新渲染,有什么办法重新渲染吗?
  • @Lei 欢迎。你能为我澄清什么不是重新渲染吗? React refs 的使用有点 react 组件生命周期之外,换句话说,refs 就像可变的“桶”,你可以在其中存储任何值,持续渲染周期.
  • 感谢您的解释,我想知道我是否可以使用 useRef 并查看是否有一种方法可以重新渲染 DOM(如 useState)?像手动调用重新渲染一样,因为更新的“输入”需要重新渲染,我不想保留 2 个副本(一个 useRef,另一个使用 useState)。
  • @Lei 哦,您是否从useState 挂钩中删除了input 状态?您仍然需要它,以便您可以更新状态并触发重新渲染。
  • @Lei 如果有兴趣,我可以向您展示一个替代方案,为了完整起见。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-04
  • 2017-06-12
  • 2019-01-30
  • 2020-12-22
  • 2020-11-08
  • 2018-06-29
  • 1970-01-01
相关资源
最近更新 更多