vogomatix
vogomatix

Reputation: 5041

React control modal from multiple components

I have an MyError modal which I need to open from various nested components.

<App>
   <Header>
      <MyError open={boolean}>
   </Header>
   <Body>
      <Component 1>
          <Inner1 opens myError>
       </Component1>
       <Component 2>
          <Inner2 opens MyError>
       </Component1>
   </Body>
</App>

The actual inner component that opens MyError may be nested several layers deep. Whats the cleanest way these various inner components can open the modal (with a message)? I thought of using ref, useImperativeHandleand/or useReducer in some way but I can't think of a clean implementation. I even thought of having a route to the component and then simply adding a fragment to the URL.

No Redux or other external reducers please.

Upvotes: 1

Views: 2043

Answers (1)

Cuong Vu
Cuong Vu

Reputation: 3723

One of the simple & clean solutions would be using Context.

First, create and export a ModalContext, wrap all of your children components inside <ModalContext.Provider>, keep the isOpen and modalMessage inside React states of your <App />.

Then pass their state setter function as Context values like this:

export const ModalContext = createContext();

export default function App() {
  const [isOpen, setIsOpen] = useState(false);
  const [modalMessage, setModalMessage] = useState("default message");
  return (
    <div className="App">
      <Modal isOpen={isOpen}>
        {modalMessage}
        <br />
        <button onClick={() => setIsOpen(false)}>close</button>
      </Modal>
      <ModalContext.Provider value={{ setIsOpen, setModalMessage }}>
        <Comp1 />
        <Comp2 />
      </ModalContext.Provider>
    </div>
  );
}

Then inside one of your child components that you want to open <Modal />, import the ModalContext that exported above, use useContext to get the setter functions and use it like this:

import { useContext } from "react";
import { ModalContext } from "./App";

export function Comp1() {
  const modalContext = useContext(ModalContext);
  return (
    <button
      onClick={() => {
        modalContext.setIsOpen(true);
        modalContext.setModalMessage("Component 1");
      }}
    >
      Open modal 1
    </button>
  );
}

Working example

Here I'm using react-modal for simplicity, but with your own <Modal />, the idea should be the same.

Upvotes: 3

Related Questions