Gabriel Geelario
Gabriel Geelario

Reputation: 237

How to open modal from anywhere in react

I have a modal when opened, display auth user data, currently, I can only open the modal on the dashboard, but I want to be able to render it from anywhere in my application. How do I achieve this?

Dashboard

     const [visible, setVisible] = useState(false)

     const trigerModal = ()=>(
       <ModalCustom visible={visible} setVisible={setVisible}>
            <form>
                <>
                  <h3>Select an Account</h3>
                  <ul className="account">
                      {accounts && accounts.map((item, i) => (
                        <li key={item.id}>
                          <h3>{item.name}</h3>
                          <h3>{item.email}</h3>         
                          <h3> {item.phone}</h3>
                        </li>            
                      ))}
                    </ul> 
                  <br />    
                </>  
             
            </form>
          </ModalCustom>
         
      )


return(
<div>
   {trigerModal()}
    <button onClick={()=> setVisible(true)}>Open modal</button
</div>

)

Profile

how do trigger the modal from this component

Upvotes: 5

Views: 3972

Answers (3)

Adam Jenkins
Adam Jenkins

Reputation: 55643

Two statements will answer virtually every react question:

  1. Don't mutate state (not applicable here)
  2. Lift state up (this is the answer to your question).

Create a context - wrap your application in it, and have any component useContext to open a modal with whatever components you want it in:


export const ModalContext = React.createContext();

const ModalProvider = ({children}) => {
  const [modalContent,setModalContent] = useState();

  return (
     <ModalContext.Provider value={
       useMemo(() => ({
        hide:() => setModalContent(),
        open:setModalContent
       }),[]
     }>
       {modalContent ? <Modal>{modalContent}</Modal> : null}
       {children}
     </ModalContext.Provider>
  )  
}

Wrap you application in the ModalProvider component so the context will be available to all your components:

const AdminDashboard = () => (
   <ModalProvider>
    <OtherComponents/>
   </ModalProvider>
)

SomeLink, a component that is anywhere inside AdminDashboard can use React.useContext to access the state in ModalProvider

const SomeLink = () => {
   const { open } = React.useContext(ModalContext);

   return (
     <button onClick={() => open(<SomeModalContent/>)}>Click to Open!</button>
   )

}

Upvotes: 5

JWittmeyer
JWittmeyer

Reputation: 165

Not the most "react"-like solution but I don't like to solve everything "react"-like so here is how I solved it for my specific usecase.

  1. Create a class with static access.
  2. Place the modal high / outside the dom tree
  3. set the necessary access in the modal component

HelperClass

export class ModalHelper {
    private static openFunction: (state: boolean) => void;
    public static setOpenFunction(f: (state: boolean) => void) {
        ModalHelper.openFunction = f;
    }
    public static setOpen(state: boolean) {
        if (ModalHelper.openFunction) {
            ModalHelper.openFunction(state);
        } else {
            console.error("ModalHelper called before setOpenFunction")
        }
    }
}

Sample Modal component

export function ExplainerModal() {
    const [open, setOpen] = useState(false);

    useEffect(() => {
        ModalHelper.setOpenFunction(setOpen);
        return () => {
            ModalHelper.setOpenFunction(undefined);
        };
    }, []);

    return <>I'm a modal</>
}

Call from anywhere

ModalHelper.setOpen(true);

Now keep in mind this only works for one modal, but can of course be further adapted to handle multiple modals (e.g. by adding a dict for the functions etc.)

Upvotes: 0

Yoel
Yoel

Reputation: 7985

If you want to access it from anywhere You need to use Global State (like Redux or Mobx)

If you want to control this from parent component you can use useRef

Upvotes: 0

Related Questions