Skip to content

Latest commit

 

History

History
 
 

composition

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

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 App as a place where we wire stuff, as an entry point. So, it's a good place for such composition. The Header though 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 same Header component but without the Navigation. 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 Header and 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 our Header test by doing something wrong in the Navigation component which is totally misleading. (Note: while testing the shallow rendering solves this problem by rendering only the Header without its nested children.)

Using React's children API

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.

Passing a child as a property

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.