Hola usuario de nuestra página web, hemos encontrado la solución a lo que necesitas, desplázate y la hallarás un poco más abajo.
Solución:
Esto es posible.
Puede crear un gancho reutilizable llamado useComponentVisible
import useState, useEffect, useRef from 'react';
export default function useComponentVisible(initialIsVisible)
const [isComponentVisible, setIsComponentVisible] = useState(initialIsVisible);
const ref = useRef(null);
const handleHideDropdown = (event: KeyboardEvent) =>
if (event.key === 'Escape')
setIsComponentVisible(false);
;
const handleClickOutside = (event: Event) =>
if (ref.current && !ref.current.contains(event.target as Node))
setIsComponentVisible(false);
;
useEffect(() =>
document.addEventListener('keydown', handleHideDropdown, true);
document.addEventListener('click', handleClickOutside, true);
return () =>
document.removeEventListener('keydown', handleHideDropdown, true);
document.removeEventListener('click', handleClickOutside, true);
;
);
return ref, isComponentVisible, setIsComponentVisible ;
Luego, en el componente que desea agregar la funcionalidad para hacer lo siguiente:
const DropDown = () =>
const ref, isComponentVisible = useComponentVisible(true);
return (
isComponentVisible && (Going into Hiding
)
);
Encuentre un ejemplo de codesandbox aquí.
Bueno, después de luchar un poco con esto, he llegado a la siguiente solución, ADEMÁS de lo que hizo Paul Fitzgerald, y teniendo en cuenta que mi respuesta también incluye transiciones.
Primero, quiero que mi menú desplegable se cierre en ESCAPE key evento y haga clic en el exterior del mouse. Para evitar crear un useEffect por evento, terminé con una función auxiliar:
//useDocumentEvent.js
import useEffect from 'react'
export const useDocumentEvent = (events) =>
useEffect(
() =>
events.forEach((event) =>
document.addEventListener(event.type, event.callback)
)
return () =>
events.forEach((event) =>
document.removeEventListener(event.type, event.callback)
)
,
[events]
)
Después de eso, useDropdown hook que trae toda la funcionalidad deseada:
//useDropdown.js
import useCallback, useState, useRef from 'react'
import useDocumentEvent from './useDocumentEvent'
/**
* Functions which performs a click outside event listener
* @param * initialState initialState of the dropdown
* @param * onAfterClose some extra function call to do after closing dropdown
*/
export const useDropdown = (initialState = false, onAfterClose = null) =>
const ref = useRef(null)
const [isOpen, setIsOpen] = useState(initialState)
const handleClickOutside = useCallback(
(event) =>
if (ref.current && ref.current.contains(event.target))
return
setIsOpen(false)
onAfterClose && onAfterClose()
,
[ref, onAfterClose]
)
const handleHideDropdown = useCallback(
(event) =>
if (event.key === 'Escape')
setIsOpen(false)
onAfterClose && onAfterClose()
,
[onAfterClose]
)
useDocumentEvent([
type: 'click', callback: handleClickOutside ,
type: 'keydown', callback: handleHideDropdown ,
])
return [ref, isOpen, setIsOpen]
Finalmente, para usar esto (tiene un estilo de emoción):
//Dropdown.js
import React, useState, useEffect from 'react'
import styled from '@emotion/styled'
import COLOR from 'constants/styles'
import useDropdown from 'hooks/useDropdown'
import Button from 'components/Button'
const Dropdown = ( children, closeText, openText, ...rest ) =>
const [dropdownRef, isOpen, setIsOpen] = useDropdown()
const [inner, setInner] = useState(false)
const [disabled, setDisabled] = useState(false)
const timeout = 150
useEffect(() =>
if (isOpen)
setInner(true)
else
setDisabled(true)
setTimeout(() =>
setDisabled(false)
setInner(false)
, timeout + 10)
, [isOpen])
return (
inner && children
)
const DropdownContainer = styled.div(
position: 'absolute',
backgroundColor: COLOR.light,
color: COLOR.dark,
borderRadius: '2px',
width: 400,
boxShadow: '0px 0px 2px 0px rgba(0,0,0,0.5)',
zIndex: 1,
overflow: 'hidden',
right: 0,
,
(props) => (
transition: props.isVisible
? `all 700ms ease-in-out`
: `all $props.timeoutms ease-in-out`,
maxHeight: props.isVisible ? props.maxHeight )
)
export Dropdown
Y, para usarlo, simplemente:
//.... your code
//... more code
Los créditos para esta solución son para la respuesta anterior aquí, esta entrada en medio y este artículo.
Puntuaciones y reseñas
Acuérdate de que tienes autorización de añadir un enjuiciamiento correcto si chocaste tu cuestión justo a tiempo.