No more boilerplate!

While I am in a tidying mood, that ConnectedPost looks a lot like boilerplate. Surely we can extract some of that away?

Here’s how it looks now.

export default class ConnectedPost extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      post: {}
    }
  }

  render() {
    const {post} = this.state
    return <Post post={post} />
  }

  async componentDidMount() {
    const post = await fetchPost(1)
    this.setState({post})
  }
}

It’s all just xxxxx post xxx Post …..fetchPost. Maybe that’s the start of a framework? Let’s try a higher-order component (HOC) the way that Redux does for connected components.

I want a function that will wrap my Post component and call fetchPost when it mounts. I’ll call it like this:

export default connect(Post, fetchPost)

I don’t know how to do this incrementally so I’ll just go for it.

import React from 'react'

export function connect(WrappedComponent, fetch) {
  return class extends React.Component {
    constructor(props) {
      super(props)

      this.type = WrappedComponent.name.toLowerCase()
      this.state = {
        model: {}
      }
    }

    render() {     
      const props = {
        [this.type]: this.state.model
      }
      return <WrappedComponent {...props} />
    }

    async componentDidMount() {
      const model = await fetch(1)
      this.setState({model})
    }
  }
}

It does exactly the same as it did before except, now it will do it for any model-view-api, not just for Posts.

Wait! What is that “1” doing in there? That could just be a property on the WrappedComponent.

    async componentDidMount() {
      const model = await fetch(this.props.id)
      this.setState({model})
    }

While we are here, api.js is a bit boilerplatey too. Can we can use convention-over-configuration to fetch models too?

We can. All of my rails routes take the form `/:pluralized_model_name/:id’. Let’s use that to make a generalized fetch() function.

import {server} from 'remote/server'

export function fetch(type, id) {
  return server.send(urlFor(type, id))
}

function urlFor(type, id) {
  return `/${type}s/${id}.json`
}

And we can call it with…

    async componentDidMount() {
      const model = await fetch(this.type, this.props.id)
      this.setState({model})
    }

I think that's enough tidying for now. Let's get back to building features for our blogging platform.

Published by

Kevin

I've been coding professionally for 30 years, 25 of those in Silicon Valley. I'm home now in beautiful Bristol, England.

Leave a Reply

Your email address will not be published. Required fields are marked *