Reputation: 5559
I have a parent jsx component that has 2 different jsx components within it. The one component is a button and the other is a div that opens and closes itself when you click on it (it has a click handler and a state of open or closed). I now want to add the ability for the button to open and close the div as well. Is the only way to accomplish this is to pass a handler function down to the button from the parent, moving the div’s open and closed state to the parent component, and pass the state down to the div as props? The reason I ask is that this particular div component is used in a number of different components and removing the open and closed state would affect a lot of different parent components.
Upvotes: 0
Views: 327
Reputation: 2236
Here's a code example of allowing external state manipulation where you can mix the usage of the button or the div to toggle the state. You extend your Collapsible component to use passed props to update the state.
class Collapsible extends React.Component {
constructor(props){
super(props);
this.state = { isOpen: this.props.isOpen !== false };
this.toggleOpen = this.toggleOpen.bind(this);
}
componentWillReceiveProps({ isOpen }) {
this.setState({ isOpen });
}
toggleOpen(){
this.setState((prevState) => ({ isOpen: !prevState.isOpen }))
}
render() {
let display = this.state.isOpen ? null : "none";
return (
<div
className="collapsible"
onClick={this.toggleOpen}
>
<header> header </header>
<div style={{ display }}>{this.props.children}</div>
</div>
);
}
}
class Parent extends React.Component {
constructor(props){
super(props);
this.state = { isOpen: true };
this.toggleOpen = this.toggleOpen.bind(this);
}
toggleOpen(){
this.setState((prevState) => ({ isOpen: !prevState.isOpen }))
}
render() {
return (
<div className="parent">
<Collapsible isOpen={this.state.isOpen}>content</Collapsible>
<button onClick={this.toggleOpen}>
toggle
</button>
</div>
);
}
}
Upvotes: 1
Reputation: 2254
Here is another code example, hope it helps:
I use the local state of the Container
and pass this down to the child components. In a bigger app I'd advice you to use something like Redux to manage your state.
The central idea is that the parent component passes a function which can "change it's state" to the button child. It also passed the current isOpen
state to the panel. Clicking the button will change the state of the parent, thus triggering a reflow, thus updating the collapsable.
For future reference:
import React from "react";
import { render } from "react-dom";
const Collapsable = ({ isOpen }) =>
isOpen ? (
<div
style={{
border: "1px solid orange",
padding: "1rem",
background: "maroon",
color: "white"
}}
>
{" "}
Hey, I'm open{" "}
</div>
) : (
<div>Oh no...closed :(</div>
);
const Button = ({ openPanel }) => (
<button onClick={() => openPanel()}>Open Panel</button>
);
class Container extends React.PureComponent {
state = { open: false };
openPanel = () => {
this.setState({
...this.state,
open: this.state.open ? false : true
});
};
render() {
return (
<div>
<Button openPanel={this.openPanel} />
<Collapsable isOpen={this.state.open} />
</div>
);
}
}
const App = () => (
<div>
<Container />
</div>
);
render(<App />, document.getElementById("root"));
Upvotes: 0