Reputation: 947
I am creating a custom React Component for a Dropdown menu. The structure I had in mind to use this component was something among the lines of
<Dropdown>
<DropdownTrigger> // The button that triggers the dropdown to open or close
open me
</DropdownTrigger>
<DropdownButton> // A button inside the dropdown
Option 1
</DropdownButton>
</Dropdown>
The way I wanted to implement this, is by having the Dropdown
component check if there is a DropdownTrigger
component in its children, and if there is, clone the given DropdownTrigger
component using React.cloneElement
and pass an onClick
property, that calls a state update on the Dropdown
component.
Small code snippet of Dropdown.js
import DropdownTrigger from './DropdownTrigger';
_toggleDropdown () {
this.setState({
isOpen: !this.state.isOpen
});
}
_renderTriggerButton () {
const triggerChild = this.props.children.find(child => child.type === DropdownTrigger);
return React.cloneElement(triggerChild, {
...triggerChild.props,
onClick: () => this._toggleDropdown()
});
}
Is this a correct approach and if so, what would be the cleanest/nicest possible way to validate the Dropdown
component has a DropdownTrigger
as child. As this means a developer always has to include a DropdownTrigger
as child of the Dropdown
component, I would like to have a nice way to tell developer they should pass a <TriggerButton>
component as child of the dropdown.
I'm also open for suggestions about changes in the structure. Thanks!
Upvotes: 4
Views: 1455
Reputation: 20027
Use React.cloneElement to pass additional properties to child components. In combination with instanceof you can do exactly what you want.
It could be something along the lines...
import React from 'react';
import DropdownTrigger from './DropdownTrigger';
class DropDown extends React.Component {
constructor(props) {
super(props);
}
...
render() {
return (
<div>
{React.Children.map(this.props.children, child => {
const additionalProps = {}
// add additional props if it's the DropdownTrigger
if (child instanceof DropdownTrigger) {
additionalProps.toggleDropDown = () => { } ...
}
return React.cloneElement(child, additionalProps);
})}
</div>
);
}
}
export default DropDown;
Upvotes: 4