Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTML Dialog Element in React.js

Im remaking one of my older projects using react+TS+Tailwind and I decided to use element for modal instead of creating classic modal element - I choose it due to accessibility.

However I ran into some issues. Currently when I can set this component to open when it uses open attribute, but then it opens as dialog and not as modal. I do not know where to call .showModal() method and on what exactly to open it as a modal (heh this seems to be easier for me to do in vanilla js :P)

Thats my Modal component

interface Props {
    showModal: HandleModal
    edit: (id: number, value: string) => void
    closeModal: React.Dispatch<React.SetStateAction<HandleModal>>
}

const Modal = ({ showModal, edit, closeModal }: Props) => {
    let modalContent = (
        <div className=''>
            <p>Please input a correct task in the input field</p>
            <Button click={() => closeModal({ show: false })}>Close</Button>
        </div>
    )

    let modalTitle = 'Error'
    return (
        <dialog aria-modal='true' open={showModal.show} className='dialog'>
            <div className=''>
                <h1>{modalTitle}</h1>
            </div>
            {modalContent}
        </dialog>
    )
}
export default Modal

And thats my App file

function App() {
    const [toDos, setToDos] = useState(DummyToDos)
    const [showModal, setShowModal] = useState<HandleModal>({
        show: false,
    })

    const addToDo = (todo: Todo) => {
        setToDos(prevState => {
            return [todo, ...prevState]
        })
    }

    const editToDo = (id: number, value: string) => {
        const editedToDos = [...toDos].map(todo => {
            if (todo.id === id) {
                todo.text = value
            }
            return todo
        })
        setToDos(editedToDos)
        setShowModal({
            show: false,
        })
    }

    const deleteToDo = (id: number) => {
        const newToDos = toDos.filter(todo => todo.id !== id)
        setToDos(newToDos)
    }
    const completeToDo = (id: number) => {
        const completedToDos = [...toDos].map(todo => {
            if (todo.id === id) {
                todo.complete = !todo.complete
            }
            return todo
        })
        setToDos(completedToDos)
    }

    return (
        <div className='mx-auto flex h-96 w-10/12 flex-col items-center text-center'>
            <Header add={addToDo} showModal={setShowModal} />
            <ToDoList items={toDos} edit={setShowModal} complete={completeToDo} del={deleteToDo} />

            <Modal edit={editToDo} showModal={showModal} closeModal={setShowModal}/>
        </div>
    )
}

export default App

I can open this component chaning open attribute to true but it opens as dialog and not modal. I've tried setting aria-modal to true to force it to open as modal - sadly it doesnt work. I do not know where and on what exactly should I call .showModal() method

like image 606
Jakub Kamiński Avatar asked Sep 30 '26 13:09

Jakub Kamiński


1 Answers

You need to have a ref to that dialog element and close or open dialog depending on your showModal.show state

const dialogRef = useRef<HTMLDialogElement>(null)

useLayoutEffect(() => {
    if (dialogRef.current?.open && !showModal.show) {
      dialogRef.current.close()
    } else if (!dialogRef.current?.open && showModal.show) {
      dialogRef.current.showModal()
    }
}, [showModal.show])

// ...

return (
   <dialog ref={dialogRef}>
    ...
   </dialog>
)
like image 128
Doğukan Akkaya Avatar answered Oct 02 '26 02:10

Doğukan Akkaya