Реакция переключает выпадающий список только один раз - PullRequest
0 голосов
/ 15 апреля 2020

Я пытаюсь построить компонент React Dropdown, используя ловушку useRef и Typescript: он открывается правильно и закрывается, если я нажимаю кнопку переключения один раз или нажимаю вне ее, но он закрывается, когда я хочу открыть ее снова. Любые идеи ? Это я как-то теряю ref referance?

Вот использование:

https://codesandbox.io/s/react-typescript-obdgs

import React, { useState, useRef, useEffect } from 'react'
import styled from 'styled-components'

interface Props {}

const DropdownMenu: React.FC<Props> = ({ children }) => {
  const [menuOpen, toggleMenu] = useState<boolean>(false)
  const menuContent = useRef<HTMLDivElement>(null)

  useEffect(() => {
    // console.log(menuOpen)
  }, [menuOpen])

  const showMenu = (event: React.MouseEvent) => {
    event.preventDefault()
    toggleMenu(true)
    document.addEventListener('click', closeMenu)
  }

  const closeMenu = (event: MouseEvent) => {
    const el = event.target
    if (menuContent.current) {
      if (el instanceof Node && !menuContent.current.contains(el)) {
        toggleMenu(false)
        document.removeEventListener('click', closeMenu)
      }
    }
  }

  return (
    <div>
      <button
        onClick={(event: React.MouseEvent) => {
          showMenu(event)
        }}
      >
        Open
      </button>
      {menuOpen ? <div ref={menuContent}>{children}</div> : null}
    </div>
  )
}

export default DropdownMenu

Ответы [ 2 ]

1 голос
/ 15 апреля 2020

Если вы дважды нажмете кнопку, вы не сможете открыть ее снова. Если вы щелкнете за пределами кнопки, чтобы закрыть ее, она будет работать должным образом.

Вероятно, это связано с тем, что ваш обратный вызов showMenu выполняется, даже когда меню уже отображается, что приводит к подключению нескольких слушателей события closeMenu, что в Поворот приводит к странному поведению.

Слушатель события closeMenu должен быть создан внутри эффекта, а не в обратном вызове showMenu.

const showMenu = (event: React.MouseEvent) => {
    event.preventDefault()
    toggleMenu(true)
}

// closeMenu is the same
const closeMenu = (event: MouseEvent) => {
    const el = event.target
    if (menuContent.current) {
        if (el instanceof Node && !menuContent.current.contains(el)) {
            toggleMenu(false)
            document.removeEventListener('click', closeMenu)
        }
    }
}

useEffect(() => {
    if (!menuOpen) {
        return
    }
    document.addEventListener('click', closeMenu)
    return () => {
        document.removeEventListener('click', closeMenu)
    }
}, [menuOpen])

useEffect действительно круто - возвращенная функция, в которой слушатель события Удалено будет вызываться как при изменении menuOpen, так и при размонтировании компонента. В предыдущем коде, если компонент был размонтирован, прослушиватель событий не будет удален.

0 голосов
/ 15 апреля 2020

Проблема исходит от вашего onClick на кнопку. Вы звоните, showMenu каждый раз, когда нажимаете кнопку, поэтому вы каждый раз добавляете новый прослушиватель событий.

Вы не хотите вызывать showMenu, если меню уже отображается, поэтому исправить можно:

<button onClick={(event: React.MouseEvent) => {
  if (!menuOpen) showMenu(event);
}}>
...