Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Require children of certain type in React Component

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!

like image 619
Niekert Avatar asked Oct 19 '25 01:10

Niekert


1 Answers

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;
like image 98
Lyubomir Avatar answered Oct 20 '25 15:10

Lyubomir



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!