From the blog

Elegant React Component APIs with Functions as Children

react

We can develop some pretty elegant React Component APIs using functions as children in React Components. Take for example a Dropdown component. If we want it to be flexible by leaving the DOM structure up to the user, we would need some way to designate toggler elements of the Dropdown. One way is with the data-toggle attribute:

Open

However, this will require manually setting up event listeners on real DOM nodes, for example, the componentDidMount method on this component might look something like this:

componentDidMount() {
  const togglers = ReactDOM
    .findDOMNode(this)
    .querySelectorAll('[data-toggle]');
  Array.prototype.forEach.call(togglers, toggler => {
    toggler.addEventListener('click', this.toggle);
  });
},

A more elegant solution is to expose the component’s toggle method to its children by using a function as a child:

  {toggle =>

Open

 

}


This way, we’re using React’s event system instead of raw DOM events, and we wouldn’t need to implement componentDidMount at all.
When toggle is called, the opened CSS class will be toggled on the div element. In other words, the component would generate DOM that looks like this:

 

and when the toggle function is called, the opened class is added to the element:

 

The implementation of this Dropdown component looks like this:

const cx = require('classnames');
const enhanceWithClickOutside = require('react-click-outside');
const React = require('react');
const Dropdown = React.createClass({
  getInitialState() {
    return {
      opened: false,
    };
  },
  handleClick(e) {
    // Close dropdown when clicked on a menu item
    if (this.state.opened && e.target.tagName === 'A') {
      this.setState({ opened: false });
    }
  },
  handleClickOutside(e) {
    if (!this.state.opened) return;
    this.setState({ opened: false });
  },
  toggle() {
    this.setState({ opened: !this.state.opened });
  },
  render() {
    const child = this.props.children(this.toggle);
    return React.cloneElement(child, {
      className: cx(
        child.props.className,
        'dropdown',
        this.state.opened && 'opened'
      ),
      onClick: this.handleClick,
    });
  },
});
module.exports = enhanceWithClickOutside(Dropdown);

this.props.children is the function child of the Dropdown component, and it is called with the instance’s toggle method. This returns a React Element, the div, which we clone to add the dropdown and opened css classes.
A discussion of this pattern and other real world use cases can be found here.

Related articles

Predicting the Future of Prediction

by Clayton Grow, PE, Account Engineer Convergent Science, Inc. Mel Brooks and the Paradox of Motion             “We can’t stop; it’s too dangerous; we’ve got to slow down first!”                         –Colonel Sandurz, in the Mel Brooks film, Spaceballs (1987) This […]

read more »

Rescale: 2014 in Review

Team 2014 was a year of immense growth and development for Rescale. Our team expanded with the addition of several new members, including, Engineer: Mairi Macdonald, Hiraku Nakamura, Alex Kudlick, Mark Whitney, and Kenneth Chung, Operations Manager: Sarah Dietz, VP […]

read more »