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
.
import Component from '@ember/component';
export default Component.extend({
mouseMove(event) {
this.set('event', event);
},
});
and the mouse-event
component's template:
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.