简体   繁体   中英

removeEventListener doesn't work on Modal close

I am building a React app which uses Semantic UI React. I have some thumbnails which open to the Modal component to show the full-sized image in the modal. I also have buttons for navigating through the next and previous. Along with the button clicks, I would like to use arrow keys to navigate. I can add event listener to the window when modal opens. But when I close the modal and open another, a duplicate event listener added to the window. This means that I need to remove the event listener when the modal is closed. However, the onClose and onUnmount props of the Semantic UI React Modal component do not apply the removal. How can I get it to apply? Anything other than removeEventListener can run in onClose or onUnmount, but removeEventListener doesn't run.

Here's the whole Attachment component:

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

Here, handleClose function contains the removeEventListener, but the removal is not applied to window despite rest of the code in handleClose can run effectively.

The only trial I was able to run removeEventListener was to use it in the handleKeyPress function. But that's not what I want, it removes the listener after one key press. I want to be able to press keys as long as the modal is open.

How do I get removeEventListener into effect when the modal is closed?

You can use useEffect hook.

So, you need to remove add/remove eventListener from handleOpen and handleClose constants.

Example:

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

When you are opening the modal, you set the open state to true.

This effect with trigger with the change of open state.

So, when it is open, you are adding the listener and when it is closed, you are removing the listener.

Simple!

I have derived a solution from this question . Using a useEffect and removing the event listener in the cleanup function provided me with the desired results. Here is the code block:

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM