Как мне удалить окно removeEventListener с помощью React useEffect

#reactjs #react-hooks #removeeventlistener

#reactjs #реагирующие хуки #removeeventlistener

Вопрос:

В документах React Hooks показано, как удалить EventListener на этапе очистки компонента. https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect

В моем случае использования я пытаюсь удалить условие removeEventListener для свойства состояния функционального компонента.

Вот пример, когда компонент никогда не размонтируется, но прослушиватель событий должен быть удален:

 function App () {
  const [collapsed, setCollapsed] = React.useState(true);

  React.useEffect(
    () => {
      if (collapsed) {
        window.removeEventListener('keyup', handleKeyUp); // Not the same "handleKeyUp" :(
      } else {
        window.addEventListener('keyup', handleKeyUp);
      }
    },
    [collapsed]
  );

  function handleKeyUp(event) {
    console.log(event.key);
    switch (event.key) {
      case 'Escape':
        setCollapsed(true);
        break;
    }
  }

  return collapsed ? (
    <a href="javascript:;" onClick={()=>setCollapsed(false)}>Search</a>
  ) : (
    <span>
      <input placeholder="Search" autoFocus />amp;nbsp;
      <a href="javascript:;">This</a>amp;nbsp;
      <a href="javascript:;">That</a>amp;nbsp;
      <input placeholder="Refinement" />
    </span>
  );
}
ReactDOM.render(<App />, document.body.appendChild(document.createElement('div')));
  

(Живой пример на https://codepen.io/caqu/pen/xBeBMN )

Проблема, которую я вижу, заключается в том, что handleKeyUp ссылка внутри removeEventListener меняется каждый раз при рендеринге компонента. Функции handleKeyUp нужна ссылка на setCollapsed , поэтому она должна быть заключена в App . Перемещение handleKeyUp внутрь useEffect также, похоже, срабатывает несколько раз и теряет ссылку на оригинал handleKeyUp .

Как я могу условно отобразить window.removeEventListener с помощью React Hooks без размонтирования компонента?

Комментарии:

1. Как (на самом деле) удалить EventListeners в React

Ответ №1:

Вы можете поместить handleKeyUp функцию внутрь функции, заданной для useEffect (это рекомендуемый способ сделать это в соответствии с официальной документацией) и добавлять прослушиватель и возвращать функцию очистки только тогда, когда collapsed значение равно false.

 useEffect(() => {
  if (collapsed) {
    return;
  }

  function handleKeyUp(event) {
    switch (event.key) {
      case "Escape":
        setCollapsed(true);
        break;
    }
  }

  window.addEventListener("keyup", handleKeyUp);
  return () => window.removeEventListener("keyup", handleKeyUp);
}, [collapsed]);
  

Ответ №2:

Ответ Толле может сработать, но объявлять функцию внутри if — плохая практика.

Это затрудняет отслеживание того, когда функция объявлена, а когда нет. Также это может привести к ошибкам, поскольку функции загружаются.

Есть более аккуратный способ это исправить:

Обернув ваш обработчик событий хуком useCallback.

 const [collapsed, setCollapsed] = useState(true)

const handleKeyUp = useCallback((event) => {
    if (event.key === "Escape") {
      setCollapsed(true)
    }
}, [setCollapsed])

useEffect(() => {
    if (!collapsed) {
        window.addEventListener("keyup", handleKeyUp)
    } else {
        window.removeEventListener("keyup", handleKeyUp)
    }

    return () => window.removeEventListener("keyup", handleKeyUp)
}, [collapsed, handleKeyUp])
  
  • useCallback зависит от setCollapsed . Это гарантирует, что handleKeyUp не переопределяется при повторной загрузке компонента (что всегда происходит при изменении состояния)
  • useEffect будет условно добавлять / удалять прослушиватель событий, в противном случае события будут продолжать запускаться до тех пор, пока компонент смонтирован.

Если вы используете много обработчиков событий в useEffect, для этого есть пользовательский хук:https://usehooks.com/useEventListener /

Вот пример постеров вопросов, обновленный моим решением:https://codepen.io/publicJorn/pen/eYzwENN

Комментарии:

1. Очень полезно! Похоже, что у вас в useEffect есть дополнительные закрывающие фигурные скобки.

2. Это именно то, что я искал, большое спасибо @publicJorn!

3. Зачем ставить setCollapsed в качестве аргумента useCallback ? Разве это не было бы эквивалентно просто do [] ?

4. Что определяет event приведенный выше пример? @publicJorn

5. На самом деле ответ Толле лучше, если это возможно. Предпочитаю определять обратный вызов внутри useEffect, пока это возможно. Тогда вам не нужно беспокоиться о useCallback. Как предлагает Кент Си Доддс: «Если вам необходимо определить функцию для вызова вашего эффекта, то делайте это внутри обратного вызова эффекта, а не снаружи». epicreact.dev/myths-about-useeffect