removeEventListener не работает при модальном закрытии

Я создаю приложение React, которое использует Semantic UI React. У меня есть несколько эскизов, которые открываются в модальном компоненте, чтобы показать полноразмерное изображение в модальном окне. У меня также есть кнопки для перехода к следующему и предыдущему. Наряду с нажатием кнопок я хотел бы использовать клавиши со стрелками для навигации. Я могу добавить слушателя событий в окно при открытии модального окна. Но когда я закрываю модальное окно и открываю другое, в окно добавляется дублирующий прослушиватель событий. Это означает, что мне нужно удалить прослушиватель событий, когда модальное окно закрыто. Однако свойства onClose и onUnmount модального компонента Semantic UI React не применяют удаление. Как я могу подать заявку? Все, кроме removeEventListener, может работать в onClose или onUnmount, но removeEventListener не запускается.

Вот весь компонент Attachment:

import React, { Fragment, useState } from 'react'
import { Button, Card, Modal, Image } from 'semantic-ui-react'
import parse from 'html-react-parser'
import { IMedia } from '../../app/models/media'

interface IProps {
    attachedMedia: IMedia, 
    gallery: IMedia[], 
    featured: boolean
}

const Attachment: React.FC<IProps> = ({ attachedMedia, gallery, featured }) => {
    const [open, setOpen] = useState(false)
    const [index, setIndex] = useState(gallery.indexOf(attachedMedia))
    const [disabledNext, setDisabledNext] = useState(false)
    const [disabledPrev, setDisabledPrev] = useState(false)

    const handlePrev = () => {
        if (index > 0) {
            setIndex(index - 1)
            setDisabledNext(false)
            if (index === 1) setDisabledPrev(true)
        } 
    }

    const handleNext = () => {
        if (index < gallery.length - 1) {
            setIndex(index + 1)
            setDisabledPrev(false)
            if (index === gallery.length - 2) setDisabledNext(true)
        }
    }

    const handleClose = () => {
        setIndex(gallery.indexOf(attachedMedia))
        setDisabledPrev(false)
        setDisabledNext(false)
        setOpen(false)
        window.removeEventListener('keydown', handleKeyPress)
    }

    const handleMount = () => {
        if (index === 0) setDisabledPrev(true)
        if (index === gallery.length - 1) setDisabledNext(true)
        window.addEventListener('keydown', handleKeyPress)
    }

    const handleKeyPress = (e: KeyboardEvent) => {
        console.log(e)
    }

    return (
        <Fragment>
            {featured ? 
                (attachedMedia.media_details.sizes.medium?.source_url !== undefined) ? 
                    <Image src={attachedMedia.media_details.sizes.medium.source_url} ui={false} onClick={() => {setOpen(true)}} />
                :
                    <Image src={attachedMedia.media_details.sizes.full.source_url} ui={false} onClick={() => {setOpen(true)}} />
            :
                <Card className="attachment"><Image src={attachedMedia.media_details.sizes.thumbnail.source_url} onClick={() => setOpen(true)} /></Card>
            }
            
            <Modal open={open} centered={false} onClose={handleClose} onMount={handleMount}>
                <Modal.Header>
                    {parse(gallery[index].title.rendered)}
                </Modal.Header>
                <Modal.Content image scrolling>
                    <Image src={gallery[index].media_details.sizes.full.source_url} wrapped fluid />
                    <Modal.Description>
                        {parse(gallery[index].description.rendered)}
                    </Modal.Description>
                </Modal.Content>
                <Modal.Actions>
                    <Button disabled={disabledPrev} onClick={handlePrev}>Previous image</Button>
                    <Button disabled={disabledNext} onClick={handleNext}>Next image</Button>
                </Modal.Actions>
            </Modal>
        </Fragment>
    )
}

export default Attachment

Здесь функция handleClose содержит removeEventListener, но удаление не применяется к окну, несмотря на то, что остальная часть кода в handleClose может работать эффективно.

Единственное испытание, которое я смог запустить removeEventListener, заключалось в использовании его в функции handleKeyPress. Но это не то, что я хочу, он удаляет слушателя после одного нажатия клавиши. Я хочу иметь возможность нажимать клавиши, пока модальное окно открыто.

Как мне заставить removeEventListener действовать, когда модальное окно закрыто?


person aademirci    schedule 27.03.2021    source источник
comment
У вас есть решение здесь https://stackoverflow.com/questions/56737851/react-hooks-useeffect-for-modal-event-listener   -  person MB_    schedule 27.03.2021
comment
Спасибо, что указали на этот вопрос. Я смог найти решение оттуда!   -  person aademirci    schedule 27.03.2021


Ответы (2)


Вы можете использовать useEffect хук.

Итак, вам нужно удалить add / remove eventListener из констант handleOpen и handleClose.

Пример:

React.useEffect(() => {
    if (open) window.addEventListener('keydown', handleKeyPress);
    else window.removeEventListener('keydown', handleKeyPress);
  }, [open]
);

Когда вы открываете модальное окно, вы устанавливаете для состояния open значение true.

Это effect с триггером с изменением состояния open.

Итак, когда он открыт, вы добавляете слушателя, а когда он закрывается, вы удаляете слушателя.

Простой!

person Zunayed Shahriar    schedule 27.03.2021
comment
Я не могу объяснить, почему, ваше предложение кажется полностью логичным, но результат такой же, как я объяснил в своем вопросе. При закрытии модального окна нажатия клавиш продолжают прослушиваться. И если я открываю другое изображение, прослушиваемые нажатия клавиш дублируются. - person aademirci; 27.03.2021
comment
Не могли бы вы создать минимальный сценарий вашего проекта на StackBlitz или CodeSandbox? - person Zunayed Shahriar; 27.03.2021
comment
Я решил проблему, объединив ваше предложение с функцией очистки, спасибо: stackoverflow.com/a/66830872/4457663 - person aademirci; 27.03.2021

Я получил решение из этого вопроса. Использование useEffect и удаление прослушивателя событий в функции очистки дало мне желаемый результат. Вот блок кода:

useEffect(() => {
        if (open) window.addEventListener('keydown', handleKeyPress)

        return () => {
            window.removeEventListener('keydown', handleKeyPress)
        }
    }, [open])
person aademirci    schedule 27.03.2021