How to show Modal after clicking a button in another component in React?
Asked Answered
L

2

6

I have this two components:

import React, { useState, useEffect } from "react";
import axios from "axios";
import Button from "../../../components/Button";
import NewAreaModal from "./NewAreaModal";

function GestioneAree() {

    const [aree, setAree] = useState([]);
    const [show, setShow] = useState(false);

    useEffect(() => {
        axios.get("http://localhost:8080/aree/all").then((res) => {
            setAree(res.data);
            console.log(res.data);
        });
    }, []);

    const showModal = () => {
        setShow(true);
    }

    return (
        <div className="bg-white rounded-lg">
            <div className="px-4 py-5 sm:px-6 rounded">
                <div className="-ml-4 -mt-2 flex items-center justify-between flex-wrap sm:flex-nowrap">
                    <div className="ml-4 mt-2">
                        <h3 className="text-lg leading-6 font-medium text-gray-900">Aree nel Sistema</h3>
                    </div>
                    <div className="ml-4 mt-2 flex-shrink-0">
                        <Button type="button" decoration="primary" text="Crea Nuova Area" onClick={showModal}/>
                    </div>
                </div>
            </div>
            <div className="flex flex-col">
                <div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
                    <div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
                        <div className="shadow overflow-hidden border-t border-gray-200 sm:rounded-lg">
                            <table className="min-w-full divide-y divide-gray-200">
                                <thead className="bg-gray-50">
                                    <tr>
                                        <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                                            Toponimo
                                        </th>
                                        <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                                            Territorio
                                        </th>
                                        <th scope="col" className="relative px-6 py-3">
                                            <span className="sr-only">Edit</span>
                                        </th>
                                    </tr>
                                </thead>
                                <tbody>
                                    {aree.map((area) => (
                                        <tr key={area.idArea} className="bg-white border-b">
                                            <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{area.toponimo}</td>
                                            <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{area.territorio.nome}</td>
                                            <td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
                                                <button type="button" className="text-green-600 hover:text-green-900" >
                                                    Modifica
                                                </button>
                                            </td>
                                        </tr>
                                    ))}
                                </tbody>
                            </table>
                            <NewAreaModal show={show} />
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
}

export default GestioneAree;

and

import React, { useState, useRef, Fragment } from "react";
import { Dialog, Transition } from "@headlessui/react";
import Button from "../../../components/Button";

function NewAreaModal() {
    const [open, setOpen] = useState(true);

    const cancelButtonRef = useRef(null);

    return (
        <Transition.Root show={open}>
            <Dialog as="div" className="relative z-10" initialFocus={cancelButtonRef} onClose={setOpen}>
                <Transition.Child as={Fragment} enter="ease-out duration-300" enterFrom="opacity-0" enterTo="opacity-100" leave="ease-in duration-200" leaveFrom="opacity-100" leaveTo="opacity-0">
                    <div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
                </Transition.Child>
                <div className="fixed z-10 inset-0 overflow-y-auto">
                    <div className="flex items-end sm:items-center justify-center min-h-full p-4 text-center sm:p-0">
                        <Transition.Child as={Fragment} enter="ease-out duration-300" enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" enterTo="opacity-100 translate-y-0 sm:scale-100" leave="ease-in duration-200" leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95">
                            <Dialog.Panel className="relative bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:max-w-lg sm:w-full">
                                <div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
                                    {
                                        // Form here
                                    }
                                </div>
                                <div className="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
                                    <Button type="button" text="Crea Area" decoration="primary" otherCSS={"w-full justify-center sm:ml-3 sm:w-auto sm:text-sm"} onClick={() => setOpen(false)}/>
                                    <Button type="button" text="Annulla" decoration="secondary" otherCSS={"w-full justify-center sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"} onClick={() => setOpen(false)} ref={cancelButtonRef}/>
                                </div>
                            </Dialog.Panel>
                        </Transition.Child>
                    </div>
                </div>
            </Dialog>
        </Transition.Root>
    );
}

export default NewAreaModal;

What I want to do is opening the component NewAreaModal.js when I'm clicking the Crea Nuova Area button... what is wrong with my code?

I have tried many ways but it dosen't work. For example, I tried to pass a prop to NewAreaModal called show and then I putted this variable inside useState in const [open, setOpen] = useState(show) but the <Transition> component says that is missing the show prop even if I passed a boolean variable...

This code is missing some functions for the comunication with the backend because I just started!

I'm practicing React since 2 months so I don't have a lot of experience...

Thank you guys for the patience!

Leannaleanne answered 1/6, 2022 at 9:47 Comment(0)
C
3

Your parent component seems ok to me. It would say the issue comes from how you handle the button interaction and props in the child.

I would start by trying something like that :

Parent:

   import NewAreaModal from "./NewAreaModal";

function GestioneAree() {

    const [aree, setAree] = useState([]);
    const [show, setShow] = useState(false);

    useEffect(() => {
        axios.get("http://localhost:8080/aree/all").then((res) => {
            setAree(res.data);
            console.log(res.data);
        });
    }, []);

    const showModal = () => {
        setShow(true);
    }

    return (
        <div className="bg-white rounded-lg">
            <div className="px-4 py-5 sm:px-6 rounded">
                <div className="-ml-4 -mt-2 flex items-center justify-between flex-wrap sm:flex-nowrap">
                    <div className="ml-4 mt-2">
                        <h3 className="text-lg leading-6 font-medium text-gray-900">Aree nel Sistema</h3>
                    </div>
                    <div className="ml-4 mt-2 flex-shrink-0">
                        <Button type="button" decoration="primary" text="Crea Nuova Area" onClick={showModal}/>
                    </div>
                </div>
            </div>
            <div className="flex flex-col">
                <div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
                    <div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
                        <div className="shadow overflow-hidden border-t border-gray-200 sm:rounded-lg">
                            <table className="min-w-full divide-y divide-gray-200">
                                <thead className="bg-gray-50">
                                    <tr>
                                        <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                                            Toponimo
                                        </th>
                                        <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                                            Territorio
                                        </th>
                                        <th scope="col" className="relative px-6 py-3">
                                            <span className="sr-only">Edit</span>
                                        </th>
                                    </tr>
                                </thead>
                                <tbody>
                                    {aree.map((area) => (
                                        <tr key={area.idArea} className="bg-white border-b">
                                            <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{area.toponimo}</td>
                                            <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{area.territorio.nome}</td>
                                            <td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
                                                <button type="button" className="text-green-600 hover:text-green-900" >
                                                    Modifica
                                                </button>
                                            </td>
                                        </tr>
                                    ))}
                                </tbody>
                            </table>
                            <NewAreaModal show={show} setShow={(bool) => setShow(bool) />
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
}

export default GestioneAree;

Child :

import React, { useState, useRef, Fragment } from "react";
 import { Dialog, Transition } from "@headlessui/react";
 import Button from "../../../components/Button";
        
    function NewAreaModal({show, setShow}) {
    
        const cancelButtonRef = useRef(null);
   
    
        return (
            <Transition.Root show={show}>
                <Dialog as="div" className="relative z-10" initialFocus={cancelButtonRef} onClose={setOpen}>
                    <Transition.Child as={Fragment} enter="ease-out duration-300" enterFrom="opacity-0" enterTo="opacity-100" leave="ease-in duration-200" leaveFrom="opacity-100" leaveTo="opacity-0">
                        <div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
                    </Transition.Child>
                    <div className="fixed z-10 inset-0 overflow-y-auto">
                        <div className="flex items-end sm:items-center justify-center min-h-full p-4 text-center sm:p-0">
                            <Transition.Child as={Fragment} enter="ease-out duration-300" enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" enterTo="opacity-100 translate-y-0 sm:scale-100" leave="ease-in duration-200" leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95">
                                <Dialog.Panel className="relative bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:max-w-lg sm:w-full">
                                    <div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
                                        {
                                            // Form here
                                        }
                                    </div>
                                    <div className="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
                                        <Button type="button" text="Crea Area" decoration="primary" otherCSS={"w-full justify-center sm:ml-3 sm:w-auto sm:text-sm"} onClick={() => setShow(false)}/>
                                        <Button type="button" text="Annulla" decoration="secondary" otherCSS={"w-full justify-center sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"} onClick={() => setShow(false)} ref={cancelButtonRef}/>
                                    </div>
                                </Dialog.Panel>
                            </Transition.Child>
                        </div>
                    </div>
                </Dialog>
            </Transition.Root>
        );
    }
    
    export default NewAreaModal;
Caught answered 1/6, 2022 at 9:59 Comment(2)
The use of Transition.Root is beacuase I'm using Tailwind UI so some components are already builded, the only thing I do is writing some functions and customizing it. Regarding your suggestion, now is working but when I close it and then try to open it again it dosen't appear. In my opinion this happens because the open variable in NewAreaModal remains false and the father components thinks it is trueLeannaleanne
It works! The only thing that I changed because when I reload the page the modal appears for a while is in NewAreaModal the variable open. I've set it to useState(false) instead of true! Thank you so much for the patience!Leannaleanne
I
2

I have tried many ways but it dosen't work. For example, I tried to pass a prop to NewAreaModal called show and then I putted this variable inside useState in const [open, setOpen] = useState(show) but the <Transition> component says that is missing the show prop even if I passed a boolean variable...

Passing in the show prop is the right idea, but you are missing a few things, and overcomplicating in some other areas.

The code in the GestioneAree component looks correct to me - you have a show state variable that you pass in to NewAreaModal, and set this to true when the button is clicked to open the modal. This is all correct. All you need to do is to have NewAreaModal use that to determine whether to show the component's contents or not.

To do that you need to first have NewAreaModal accept show as a prop:

function NewAreaModal({ show }) {
  // component code
}

This show prop will then be the only thing that controls whether the modal is open or not. So you don't need, or want, any state here. So remove this line:

const [open, setOpen] = useState(true);

and instead use the show prop where you were using open before - in particular pass it to the Transition.Root, which is component you don't show but I assume this is what will actually show or hide the contents:

<Transition.Root show={show}>

That's essentially it, but there's another important way you will have to change your code. Your modal component appears to have a button inside it to close the modal. That would work when you had the open (or show) property in state, as you could have the button set that state to false. That doesn't work directly when show is a prop instead.

But there's an easy fix - to simply have your modal component accept a function that closes the modal.

You already have an openModal function defined in your parent component:

const showModal = () => {
    setShow(true);
}

and you can do something similar to make a "closeModal" function:

const closeModal = () => {
    setShow(false);
}

Then you can pass this function in as a prop to NewAreaModal:

<NewAreaModal show={show} closeModal={closeModal}/>

Finally, NewAreaModal needs to accept this prop:

function NewAreaModal({ show, closeModal }) {
  // component code
}

and call it when the close button is clicked, by replacing onClick={() => setOpen(false)} by onClick={closeModal} for each button where you have this.

(Note that if you are using NewAreaModal elsewhere you will need to ensure this closeModal prop is passed in each time it is used.)

Incumbent answered 1/6, 2022 at 10:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.