Reputation: 13105
I have a rather simple problem and I'm not sure how to solve it with React's one way data flow.
Say you have a link in the parent that shows a modal
In the modal, you have an "X" that closes it.
I know I can change the state of the modal from the parent via props
// In the parent
<Modal display={this.state.showModal} />
// In the modal
<div className={this.props.display ? "show" : "hide"}>
<a className="close">×</a>
...
</div>
And I know how to close the modal, but not both. Not sure how to keep a state that is shared and controllable by both the parent and the child modal.
UPDATE
In trying to keep this as modular as possible, I think the React way would be to store the open/close logic in the modal variable.
var ParentThing = React.createClass({
...
render (
<Modal /> // How can I call this.open in the modal from here?
)
});
var Modal = React.createClass({
setInitialState: function() {
return {
display: false
}
},
close: function() {
this.setState({ display: false });
},
open: function() {
this.setState({ display: true });
},
render: function() {
return (
<div className={this.state.display ? "show" : "hide"}>
<a className="close" onClick={this.close}>×</a>
</div>
)
}
});
I saw this method, but it seems to be a little more than I need to do here. Reactjs: how to modify child state or props from parent?
Upvotes: 13
Views: 7993
Reputation: 159095
There are two ways to handle this kind of thing in React:
value
and onChange
property, where the owner of the input controls the input.value
.The second choice seems faster up front, but just like managing a collection of form inputs in React, the advantage to using fully controlled components becomes apparent as complexity builds and the need to fully describe your UI at any point and time increases. (See this excellent answer from FakeRainBrigand if you're curious exactly why controlled components is better than uncontrolled in most cases.)
However, just like form inputs, there's no reason your component can't be either controlled or uncontrolled. If the user passes a display
and onClose
property, like Austin Greco's answer, you have a controlled modal, and the parent fully decides when to show or hide the modal.
If the user doesn't, you can skip using the properties and instead delegate to internal state managed by public methods on the modal component:
var ParentThing = React.createClass({
...
render: function() {
return <Modal ref="modal" />;
},
handleSomeClick: function() {
this.refs.modal.open();
}
});
var Modal = React.createClass({
setInitialState: function() {
return {
display: false
}
},
close: function() {
this.setState({ display: false });
},
open: function() {
this.setState({ display: true });
},
render: function() {
return (
<div className={this.state.display ? "show" : "hide"}>
<a className="close" onClick={this.close}>×</a>
</div>
)
}
});
If you like the idea of a controlled Modal component, but don't want to do all the boilerplate typing, you could even go so far as to implement something like the valueLink
property for the Modal to simplify this pattern.
var ParentThing = React.createClass({
...
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return { showModal: false };
},
render: function() {
return <Modal displayLink={this.linkState("showModal")} />;
},
handleSomeClick: function() {
this.setState({showModal: true});
}
});
var Modal = React.createClass({
close: function() {
this.props.displayLink.requestChange(false);
},
render: function() {
return (
<div className={this.props.displayLink.value? "show" : "hide"}>
<a className="close" onClick={this.close}>×</a>
</div>
)
}
});
(See my blog post on creating custom components that work with linkState
/valueLink
for more info.)
So now you get the benefit of using a fully parent-controlled Modal, but you've removed a portion of the boilerplate around creating a function that sets the value to false
and passing it to the modal.
Upvotes: 16
Reputation: 33544
You could pass a callback as a prop to the child component:
// In the parent
<Modal display={this.state.showModal} onClose={this.closeModal} />
// In the modal
<div className={this.props.display ? "show" : "hide"}>
<a className="close" onClick={this.props.onClose}>×</a>
...
</div>
Then when you click the close button on the child, it will call the function of the parent
Upvotes: 7