Reputation: 5041
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
, useImperativeHandle
and/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
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>
);
}
Here I'm using react-modal
for simplicity, but with your own <Modal />
, the idea should be the same.
Upvotes: 3