4

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?

aademirci
  • 96
  • 8
  • 1
    You have a solution here https://stackoverflow.com/questions/56737851/react-hooks-useeffect-for-modal-event-listener – MB_ Mar 27 '21 at 01:32
  • Thanks for pointing that question. I've been able to derive solution from there! – aademirci Mar 27 '21 at 11:31

2 Answers2

1

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!

Zunayed Shahriar
  • 2,557
  • 2
  • 12
  • 15
  • I cannot explain why, your suggestion seems completely logical, but the result is the same as I explained in my question. When closing the modal, the key presses keep being listened. And if I open another image, the listened key presses are duplicated. – aademirci Mar 27 '21 at 11:25
  • Can you please create a minimal scenario of your project at StackBlitz or CodeSandbox? – Zunayed Shahriar Mar 27 '21 at 11:35
  • I have solved the problem combining your suggestion with the cleanup function, thanks: https://stackoverflow.com/a/66830872/4457663 – aademirci Mar 27 '21 at 11:37
1

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])
aademirci
  • 96
  • 8