reactjsmodal-dialogalert

React not rendering alert when confirming comment deletion in modal


Context: interactive comment section CRUD app

When deleting a comment, the user clicks a Delete button which opens a modal to ask them to confirm the deletion. That part (the modal opening/closing and deleting the comment) works fine.

What I want to add is an alert/flash notification once the user has confirmed the deletion and the comment is removed. I was able to implement this when updating the comment, but when using the exact same method for deleting, the alert is not rendering for some reason.

I have a feeling this has something to do with the fact that this component uses a modal but I can't figure out what is blocking exactly.

What I've tried:

Here below is my code. I would be grateful if someone could help me fixing this.

const UserActionButtons = ({ comment, isEditing, isMobile, isReply, isCurrentUser, onDelete, editComment, handleClickReply}: UserActionButtonsProps) => {
    const [isDeleteModalOpen, setDeleteModalOpen] = useState<boolean>(false)

    const handleOpenConfirmationModal = (): void => {
        setDeleteModalOpen(true)
    }

    const handleCloseConfirmationModal = (): void => {
        setDeleteModalOpen(false)
    }

    return (
        <>
            {isCurrentUser ?
                (<div className={`${isEditing && isMobile ? 'hidden' : 'cta-button'}`}>
                    <div className='flex items-center fill-primary-red-soft hover:fill-primary-red-pale   text-primary-red-soft hover:text-primary-red-pale  '>
                        <DeleteIcon />
                        <p className="font-medium mr-4"
                            onClick={() => handleOpenConfirmationModal()}
                        >  Delete</p>
                    </div>
                    <DeleteConfirmationModal
                        isOpen={isDeleteModalOpen}
                        onDelete={onDelete}
                        onCancel={handleCloseConfirmationModal}
                        comment={comment}
                        isReply={isReply}
                    />
                    <div className='flex items-center cta-button-blue'>
                        <EditIcon />
                        <p className="font-medium"
                            onClick={editComment}
                        >  Edit</p>
                    </div>
                </div>
                ) : !isEditing &&
                (<div className='cta-button cta-button-blue'>
                    <ReplyIcon />
                    <p className="font-medium"
                        onClick={handleClickReply}
                    >  Reply</p>
                </div>)}
        </>
    )
}
const DeleteConfirmationModal = ({
    isOpen,
    comment,
    isReply,
    onCancel,
    onDelete
}: DeleteConfirmationModalProps) => {
    const { id } = comment
    const [alert, setAlert] = useState<AlertInterface>({ show: false, type: '', text: '' })

    const showAlert = (show = false, type = '', text = ''): void => {
        setAlert({ show, type, text })
    }

    const deleteComment = (id: number, deletedComment?: MessageMeta): void => {
        onDelete(id, deletedComment)
        showAlert(true, 'success', 'Comment deleted!')
        // console.log(alert.show)
    }

    useEffect(() => {
        console.log(alert)
    }, [alert])

    return (
        <>
            {alert.show && <Alert {...alert} showAlert={showAlert} />}
            <Modal isOpen={isOpen}>
                <article className='flex flex-col gap-y-4'>
                    <h4 className='font-medium text-xl text-neutral-blue-dark'>Delete comment</h4>
                    <p className='text-neutral-blue-grayish'>Are you sure you want to delete this comment? This will remove the comment and can't be undone.</p>
                    <div className='flex gap-x-2 justify-between'>
                        <button className='bg-neutral-blue-grayish text-white font-medium px-4 sm:px-8 py-3 uppercase rounded-lg border-none w-6/12'
                            onClick={onCancel}>No, cancel</button>
                        <button className='bg-primary-red-soft text-white font-medium px-4 sm:px-8 py-3 uppercase rounded-lg border-none w-6/12'
                            onClick={() => deleteComment(id, isReply ? comment : undefined)}
                        >Yes, delete</button>
                    </div>
                </article>
            </Modal >
        </>
    )
}
const Alert = ({
    type,
    text,
    showAlert
}: AlertProps) => {

    console.log('alert renders')

    useEffect(() => {
        const timeout = setTimeout(() => {
            showAlert()
        }, 3000)
        return () => clearTimeout(timeout)
    }, [])

    return (
        <aside className={`${type === 'success' ? 'bg-primary-green-success' : 'bg-primary-red-soft'} text-white fixed bottom-5 right-5 p-4 z-50`}>{text}</aside>
    )
}
const Modal = ({
    isOpen,
    children
}: ModalProps) => {
    const [isModalOpen, setModalOpen] = useState(isOpen)
    const modalRef = useRef<HTMLDialogElement | null>(null)

    useEffect(() => {
        setModalOpen(isOpen)
    }, [isOpen])

    useEffect(() => {
        const modalElement = modalRef.current

        if (modalElement) {
            if (isModalOpen) {
                modalElement.showModal()
            } else {
                modalElement.close()
            }
        }
    }, [isModalOpen])

    return (
        <dialog ref={modalRef}
            className={'p-4 sm:p-6 rounded-lg min-w-20 max-w-xs sm:max-w-sm cursor-default'}>
            {children}
        </dialog>
    )
}

Solution

  • I actually found the solution which is not in the code I shared above Basically because I'm recursively rendering the comments, I also needed to pass the showAlert prop to the Comment component itself

    I also lifted the state up to the highest parent

     {comment.replies?.length !== 0 ? (
                <div className='flex w-full'>
                    <div className='w-0.5 bg-neutral-gray-light md:ml-12'></div>
                    <div className='pl-6 md:pl-12 max-w-full flex-grow'>
                        {comment.replies?.map((reply) =>
                        (<Comment
                            key={reply.id}
                            comment={reply}
                            currentUser={currentUser}
                            commentList={commentList}
                            onReply={onReply}
                            onEdit={onEdit}
                            onDelete={onDelete}
                            onScoreChange={onScoreChange}
                            showAlert={showAlert}
                        />))}
                    </div>
                </div>) : null}