Better component transclusion in React with ‘outlets'
As a believer of Facebook React’s principles, here’s an explanation of how React handles transclusion, and how it can be improved.
What is transclusion? #
In terms of UI development, transclusion is when a child component is declared from inside of parent component, and the child component is agnostic to its parent. This allows for a nested, flowing and declarative component hierarchy.
Basic transclusion in React #
React is able to handle basic transclusion out of the box using the this.props.children
property, which is an array of all components that have been declared directly inside of the current component, like so:
var MyExample = React.createClass({
displayName: 'MyExample',
render: function() {
return (MyComponent(null,
React.DOM.p(null, "This paragraph is transcluded into MyComponent.")
));
}
});
As you can see, the specific rendered position of the child paragraph (which in this case is a child component of MyComponent), isn’t described by the MyExample component; only MyComponent is concerned about where it is rendered. Conversely, MyComponent would look something like this:
var MyComponent = React.createClass({displayName: 'MyComponent',
render: function() {
return (
React.DOM.div(null,
React.DOM.h1(null, "I want my sub components to go below this title."),
this.props.children
)
);
}
});
Outlets #
This is a useful technique, but it has a limitation: you can’t define multiple outlets for transclusion. For example, you may want a component that has a header and footer, as well as some body content. You want to be able to declare all of these directly in the parent component, but each part needs to be rendered into different locations of the child component.
The simplest option to allow you to define outlets in your component is by using this.props
directly, and providing your component through that, e.g.
MyComponent( {header:React.DOM.div(null, "header"), footer:Footer(null )}, "some children")
Update: I did write a mixin to use the outlets as children, and for them to get outputted before the render function of the child component. But, after discussing this people on the #reactjs IRC, it’s probably better to use outlets as simple properties (as above).
This raised an important issue when dealing with JSX: remember that whenever you write some JSX component markup, all you are doing is calling that function. So you can pass in other components as properties to that component.
Side note: React mixins are awesome
When I was writing the (now redundant) mixin above, I didn’t realise how React handles lifecycle methods of a component which has attached mixins. I searched for a way to call the equivalent parent methods from within the mixed component. Well, it turns out that React automatically runs all lifecycle methods from all mixins and the component. Boom!
So yeah, you can now have fun with more outlet-led transclusion, and create some reusable interfaces.