React Patterns in Ember - Render Props

Last reviewed on February 25, 2018

Lately I've been hearing a lot about various component patterns in React. One pattern that has been coming up is the Render Props pattern. I was curious about what problem it solves and what the equivalent is in Ember.

TL;DR - An Ember component in its block form yielding state through a block param can be similar to a single render prop. Contextual components can be used similarly to multiple render props in React, with a few minor differences.

The React docs describe Render Props as:

A render prop is a function prop that a component uses to know what to render

Here is an example from the React documentation:

<Mouse render={mouse => <Cat mouse={mouse} />} />

The Mouse component implements some behavior but delegates the rendering to a function prop passed in, which in this example is called render (it doesn't have to be called render, but it is a common convention that libraries like downshift follow). If you aren't familiar with React, you can have plain JavaScript functions that return JSX. These types of components are called Stateless Functional Components, as opposed to the stateful components using classes that extend from React.Component.

Here are the Mouse and Cat components for the snippet above:

class Mouse extends React.Component {
  constructor(props) {
    super(props);
    this.handleMouseMove = this.handleMouseMove.bind(this);
    this.state = { x: 0, y: 0 };
  }

  handleMouseMove(event) {
    this.setState({
      x: event.clientX,
      y: event.clientY,
    });
  }

  render() {
    return (
      <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
        {this.props.render(this.state)}
      </div>
    );
  }
}

class Cat extends React.Component {
  render() {
    const mouse = this.props.mouse;
    return (
      <img
        src="/cat.jpg"
        style={{ position: 'absolute', left: mouse.x, top: mouse.y }}
      />
    );
  }
}

Another way you might see this pattern in React is using the children prop, which could look like this:

<Mouse
  children={mouse => (
    <p>
      The mouse position is {mouse.x}, {mouse.y}
    </p>
  )}
/>

or like this:

<Mouse>
  {mouse => (
    <p>
      The mouse position is {mouse.x}, {mouse.y}
    </p>
  )}
</Mouse>

In React, the stuff between the opening and closing tag of a component becomes a prop called children. In the Mouse class, the only change would be that this.props.render(this.state) becomes this.props.children(this.state).

So what would this look like in Ember? In Ember, you can achieve this using a component in its block form with a block param:

{{#mouse-event as |mouse|}}
  <p>The mouse position is {{mouse.x}}, {{mouse.y}}</p>
{{/mouse-event}}

Currently in Ember, component names must have a hyphen, so I've changed the name from Mouse in the React example to mouse-event.

app/components/mouse-event.js
import Component from '@ember/component';

export default Component.extend({
  mouseMove(event) {
    this.set('event', event);
  },
});

and the mouse-event component's template:

{{yield (hash x=event.clientX y=event.clientY)}}

We can yield state from within the mouse-event component back to the scope that invoked the component as a block parameter called mouse. This ultimately lets the consumer of the component control what to render.