February 08, 2017

How to Dynamically Render React Components

edit

I am currently working on a React/Redux universally rendered application at work. It has some fun parts and I want to share what I’ve learned from building them.

One of those parts is a component I have called the BlocksLoop. In the design phase of the project, long before I was ever a part of the project, the designers and back end dev had the foresight to create a system of reusable admin components. They called them “blocks”. Each page of the application was to be architected by utilizing these blocks in different orders and patterns. This was a front-end developer’s dream come true. I considered crying, but then didn’t, mostly because I can’t. Seriously. I have some weird dry eye condition. I don’t create the same amount of tears as you normal, emotionally and physically healthy people. But I digress.

In the end, the designers came up with 10 reusable blocks for the project. The backend developer implemented an API that returned an array of objects. So, how do we handle rendering components when we have no idea how many, or specifically which ones we’ll need at any given time?

In my last blog post, I showed you how we can render different components conditionally. We’re going to take that a step further. Actually we’re going to take that to 11.

Inspired by the architecture of reducers in Redux, I realized a simple way to handle this very dynamic array of objects was to use a switch statement.

I knew that each object in the array had a type property. I needed to make individual components for each type. I could then check this type, and require the proper component with each one. This is significantly easier architecture than attempting to write a giant component to handle all the types. So let’s see how this works.

For this example, imagine that I have several built out components. Each of these components represents a type of block. We’re going to import these into what will become our BlocksLoop component. I’ll also setup our component to accept a blocks prop, but it won’t do anything important yet.

import React, { Component, PropTypes } from 'react'
import HeadingBlock from './HeadingBlock'
import TextBlock from './TextBlock'
import ImageBlock from './ImageBlock'
import ListBlock from './ListBlock'

export default class BlocksLoop extends Component {
  render() {
    return (
      <div className="blocks_loop">
        {this.props.blocks.map(block => (
          <div className="block" />
        ))}
      </div>
    )
  }
}

Right now, I’m just returning all the blocks as a simple div and not utilizing the imported individual blocks. Let’s solve that with a switch statement. To make it cleaner, we’ll move this logic into a method on the component that’s called each time a block item is mapped over.

// ...

export default class BlocksLoop extends Component {
  constructor() {
    super()
    this.getBlockComponent = this.getBlockComponent.bind(this)
  }

  getBlockComponent(block) {
    switch (block.type) {
      case 'heading':
        return <HeadingBlock key={block.id} {...block} />

      case 'text':
        return <TextBlock key={block.id} {...block} />

      case 'image':
        return <ImageBlock key={block.id} {...block} />

      case 'list':
        return <ListBlock key={block.id} {...block} />

      default:
        return <div className="no_block_type" />
    }
  }

  render() {
    return (
      <div className="blocks_loop">
        {this.props.blocks.map(block => this.getBlockComponent(block))}
      </div>
    )
  }
}

And there you have it! Our blocks array is mapped over, each item is passed into our method, and the correct component is returned dynamically. If for some reason a type was added in the back end before I could make a new component for it, the method returns an empty div with a class I can style.

I think it might be possible to reduce some of the boilerplate in the switch statement by creating a variable, such as let dynamicComponent = null, and then overriding that assignment in each case with the correct component. Then in one last step in the function, I could add the key and {...block} props (via the Object spread operator) to the currently assigned dynamicComponent variable. I’m ok with being this explicit for now. It’s very legible and understandable.

If you find this useful, I’d love to hear about it in the comments. Or if you have a way to improve this, I’d love to hear that, too.


Liked the post?
Give the author a dopamine boost with a few "beard strokes". Click the beard up to 50 times to show your appreciation.
Kyle Shevlin's face, which is mostly a beard with eyes

Kyle Shevlin is the founder & lead software engineer of Agathist, a software development firm with a mission to build good software with good people.

Logo for Array.reduce()
Array.reduce()
Check out my courses!
If you enjoy my posts, you might enjoy my courses, too. Click the button to view the course or go to Courses for more information.
Sign up for my newsletter
Let's chat some more about TypeScript, React, and frontend web development. Unsubscribe at any time.