React Composition Patterns From The Ground Up

This post explores the many ways React Components can be combined, mixed-in, and mixed-up, including:

  • The Lifting State & Container/Presenter Pattern
  • Higher-Order Components
  • Render Callbacks/”Function-as-Child”
  • “Renderless” State Providers

Building Blocks

Elements

Arguments to createElement:

  • type: DOM element name ("h1"), or handle to a function that creates elements (see Components below).
  • props: Properties to add to the DOM Element's attributes, or pass to the Component function.
  • ...props.children: any additional arguments are combined into an array called props.children. Nested arrays are flattened.

Components

  • They must implement a render method that provides the return value as above
  • The component receives its initial prop arguments in the constructor and binds them as this.props; this.state can also be initialized at this point
  • render is called against a component instance so it has access to its props, state, and other properties
  • The component responds to changes in props by calling lifecycle methods such as componentWillReceiveProps (see Understanding React - Component Lifecycle)
  • If the component’s state or props change and all lifecycle hooks allow it, a re-render is scheduled and render is called again, returning a new virtual DOM tree
  • The virtual DOM returned by the component is compared against the actual DOM and any changes are synced to it. This is the crux of the React programming model, where the layout engine takes a declarative description of what the DOM should look like and “makes it so”.

Unlike the static example in the Elements section, this HTML will update if Baz changes its state.

JSX

Read JSX In Depth and WTF is JSX? for a deep dive into what you can and can’t do with JSX.

Composition Patterns

Lifting State & Container/Presenter

  1. Divide components into stateful “containers” and stateless “presenters”.
  2. Pass functions (“callbacks”) that change the container state as props to children
  3. If two components need access to the same state, move the state into their common parent

The React docs have a good explanation of how to structure components in this way: Lifting State Up

Higher-Order Component (HOC)

Think of an HOC as a component factory or a stage in a “component assembly line”.

Without JSX it is technically possible to use HOCs inline as well, but this will cause a re-paint every render because a new component class is created each time and React uses the identity of the component constructor as part of its reconciliation.

Examples

  • React-Redux uses an HOC called connect to map store state to props
  • React-Router’s withRouter HOC provides route context to components needing access to history APIs

Disadvantages

  • Creates a wrapper around components, allocating a new function and taking up space in the tree when debugging
  • Higher-order function composition doesn’t always work inline with JSX, depending on what you’re trying to do.

Render Prop/Function-as-Child

See Use A Render Prop and Function as Child Component for an in-depth discussion of the approach.

Note: Render props should be called as plain functions, not components with JSX/createElement. React uses the identity of the component constructor as part of its reconciliation:

❌ Don’t do: const Comp = props.render; return <Comp x={y} />
✅ Do: return props.render(y)

Disadvantages

  • Function signature is difficult to enforce (static checking works, using PropTypes at runtime is impossible)
  • For the function-as-child pattern, implicitly redefining the meaning of the 3rd argument (children) in createElement can be confusing

“Renderless” State Provider

DIY Example: The snippet below uses higher-order functions and inheritance to compose state containers with arbitrary stateless components. The combine(Container, Presenter) higher-order function allows components that extend the StateContainer utility class to provide state to stateless functions.

Discuss

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store