React in patterns / Composition
One of the biggest benefits of React is composability. I personally don't know a framework that offers such an easy way to create and combine components. In this section we will explore few composition techniques which proved to work well.
Let's get a simple example. Let's say that we have an application with a header and we want to place a navigation inside. We have three React components - App, Header and Navigation. They have to be nested into each other so we end up with the following markup:
<App>
<Header>
<Navigation> ... </Navigation>
</Header>
</App>The trivial approach for combining these components is to reference them in the places where we need them.
// app.jsx
import Header from './Header.jsx';
export default class App extends React.Component {
render() {
return <Header />;
}
}
// Header.jsx
import Navigation from './Navigation.jsx';
export default class Header extends React.Component {
render() {
return <header><Navigation /></header>;
}
}
// Navigation.jsx
export default class Navigation extends React.Component {
render() {
return (<nav> ... </nav>);
}
}However, following this pattern we introduce several problems:
- We may consider the
Appas a place where we wire stuff, as an entry point. So, it's a good place for such composition. TheHeaderthough may have other elements like a logo, search field or a slogan. It will be nice if they are passed somehow from the outside so we don't create a hard-coded dependency. What if we need the sameHeadercomponent but without theNavigation. We can't easily achieve that because we have the two bound tightly together. - It's difficult to test. We may have some business logic in the
Headerand in order to test it we have to create an instance of the component. However, because it imports other components we will probably create instances of those components too and it becomes heavy for testing. We may break ourHeadertest by doing something wrong in theNavigationcomponent which is totally misleading. (Note: while testing the shallow rendering solves this problem by rendering only theHeaderwithout its nested children.)
In React we have the handy this.props.children. That's how the parent reads/accesses its children. This API will make our Header agnostic and dependency-free:
// App.jsx
export default class App extends React.Component {
render() {
return (
<Header>
<Navigation />
</Header>
);
}
}
// Header.jsx
export default class Header extends React.Component {
render() {
return <header>{ this.props.children }</header>;
}
};It's also easy to test because we may render the Header with an empty <div>. This will isolate the component and will let us focus on only one piece of our application.
Every React component receive props. It's nice that these props may contain all kind of data. Even other components.
// App.jsx
class App extends React.Component {
render() {
var title = <h1>Hello there!</h1>;
return (
<Header title={ title }>
<Navigation />
</Header>
);
}
};
// Header.jsx
export default class Header extends React.Component {
render() {
return (
<header>
{ this.props.title }
<hr />
{ this.props.children }
</header>
);
}
};This technique is helpful when we have a mix between components that exist inside the Header and components that have to be provided from the outside.