I answered a similar question a while back relating to the best way of opening/closing modal
I have spent a lot of time with React since then and learned a few lessons along the way.
I've found this general approach to work nicely for dealing with modals: Using a fully controlled "dumb" component that takes 3 props.
- show: Boolean - Is the modal visible?
- close: Function - The modal needs a callback in order to close itself
- children: node - The contents of the modal
See React Docs for info on Controlled Components
To answer your question about the difference between the two, is that IMO option 1 provides a cleaner and more flexible API to work with while option 2 is more minimalist.
With option 1 you could take care of hiding/showing by using either CSS or returning null from <Modal>. I would recommend returning null since the modal contents will simply not be rendered vs. rendering them and "hiding" them via CSS.
Option 2 forces the more verbose "JSX way" of conditionally rendering which I think is appropriate in many cases. However I feel like the concept of a modal merits the hiding/showing being a part of a <Modal> components API (props/methods/etc...)
Why pass down the close prop/callback?
Considering most modals have UX such as closing on events such as: pressing [ESC], clicking "x", clicking outside the modal, etc... a modal needs to be informed of how to "close itself" via passing down the close prop/callback in my examples below.
Code Examples
// The simple, fully controlled Modal component
const Modal = React.createClass({
render() {
const {
show, // Boolean - Is the modal visible?
close, // Function - The modal needs a function to "close itself"
children, // node - The contents of the modal
} = this.props;
return !show ? null : (
<div className="some-class-for-styling">
<a onClick={close}>x</a>
{children}
</div>
);
}
});
const UsesModal = React.createClass({
setEditing(editing) {
this.setState({editing});
},
render() {
// `editing` could come from anywhere.
// Could be derived from props,
// or managed locally as state, anywhere really....
const {editing} = this.state;
return (
<div>
<h1>Some Great Component</h1>
<a onClick={() => this.setEditing(true)}>Show Modal!</a>
<Modal show={editing} close={() => this.setEditing(false)}>
Some great modal content... show based on UsesModal.state.editing
</Modal>
</div>
);
}
});
And if you want to let the modal manage its own state, you can wrap up the "dumb" modal with a slightly smarter component and make use of refs and "public component methods" (although I've found that sticking with the simplified approach usually results in less headache and regret ;))
const SmarterModal = React.createClass({
close() {
this.setState({show: false});
},
open() {
this.setState({show: true});
},
render() {
const {children} = this.props;
const {show} = this.state;
return (
<Modal show={show} close={this.close}>
{children}
</Modal>
);
}
});
const UsesSmarterModal = React.createClass({
render() {
return (
<div>
<h1>Some Great Component</h1>
<a onClick={() => this.refs.my_smarter_modal.open()}>Show Modal!</a>
<SmarterModal ref="my_smarter_modal">
Some great modal content... show based on SmarterModals own internal state
</SmarterModal>
</div>
);
}
});
There are a number of ways you can wrap up the simple <Modal>, but I feel like it serves as a solid foundation and the data flow plays nicely to allow computing/deriving "is the modal open" from wherever makes the most sense. This is the approach I've found to work nicely.
closeprop/callback in my examples below.