Reputation: 287380
I have a situation where I have something like this:
<DropDown>
<p>Select an option:</p>
<button onClick={() => console.log("opt 1")}>Option 1</button>
<button onClick={() => console.log("opt 1")}>Option 2</button>
</DropDown>
The DropDown
component is one that I wrote, that renders this.props.children
in a drop-down fashion. The DropDown has an onClick
call that makes it close.
DropDown
looks something like this (simplified):
class DropDown extends Component {
state = {
open: false
};
render() {
return (
<div className={`drop-down ${this.state.open ? "open" : "closed"}`}>
<div className="closed-version">
<div className="header" onClick={this.open}>
<div className="header-contents">Click here to select an option</div>
</div>
</div>
<div className="open-version">
<div className="content" onClick={this.closeWithoutSelection}>
{this.props.children}
</div>
</div>
</div>
);
}
open = () => {
this.setState({ open: true }, () => {
if (this.props.onOpen) {
this.props.onOpen();
}
});
};
closeWithoutSelection = () => {
this.setState({ open: false }, () => {
if (this.props.onCloseWithoutSelection) {
this.props.onCloseWithoutSelection();
}
});
};
}
The issue I'm running into is that I want to do something different to the DropDown whether it was closed selecting an option or not. How do I go about doing that?
Upvotes: 1
Views: 128
Reputation: 6402
Accordingly @pupeno (and he is right), the dropdown logic should be within the dropdown itself. However, we should pass a callback function in order to deal with the chosen data.
class DropDown extends React.Component {
state = {
open: false,
};
toggleDropdown = (e) => {
this.setState({
open: !this.state.open,
});
const value = e.target.getAttribute('value');
if ( value !== "null") {
this.props.selectItem(value);
}
};
render() {
const { selectItem } = this.props;
const { open } = this.state;
return (
<div className={open ? "drop-down open" : "drop-down"}>
<div onClick={this.toggleDropdown} value="null">Select</div>
<div onClick={this.toggleDropdown} value="1">Item 1</div>
<div onClick={this.toggleDropdown} value="2">Item 2</div>
<div onClick={this.toggleDropdown} value="3">Item 3</div>
</div>
);
}
};
class App extends React.Component {
requestItem = (item) => {
alert(`Request item ${item}`);
};
render() {
return (
<div>
<DropDown selectItem={this.requestItem}/>
</div>
);
}
}
ReactDOM.render(
<App />,
document.querySelector('#app')
);
.drop-down {
border: 1px solid black;
width: 180px;
height: 30px;
overflow: hidden;
transition: height 0.3s ease;
}
.open {
height: 120px;
}
.drop-down > div {
padding: 5px;
box-sizing: border-box;
height: 30px;
border-bottom: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Upvotes: 1
Reputation: 20614
In your DropDown
component you have some state and you are probably rendering your children like this:
this.props.children
Instead you can use render props to pass state, methods or anything else down to your children without having to handle it outside of the DropDown component at the parent level.
class DropDown extends Component {
// constructor / state, methods...
yourSpecialMethod(item) {
// do your special thing here
}
render() {
// pass state or methods down to children!
return this.props.children(yourSpecialMethod)
}
}
Then modify your render slightly:
<DropDown>
{handleSpecialMethod =>
<>
<p>Select an option:</p>
<button onClick={() => handleSpecialMethod("opt 1")}>Option 1</button>
<button onClick={() => handleSpecialMethod("opt 1")}>Option 2</button>
</>
}
</DropDown>
Upvotes: 1
Reputation: 845
You could use an onChange function and call the parent function that is passed down as a prop. This will "affect the parent" as you asked.
this.props.onChange()
or something similar.
Upvotes: 0