Functional approach to React components (getting rid of `this`)

I don’t like this. I mean I don’t like this.

There are multiple articles on why OOP is not the best paradigm, and why it’s especially so in JavaScript.

One example is inheritance. Even Java folks know the Composition over Inheritance rule (thanks to J. Bloch?) and React team explicitly states this as well.

Another side of it is the infamous JavaScript this, which is hard to deal with and which is known for making the code flow particularly tangled and messy.

But what really bothers me is how many new developers now who start straightaway with React skipping the basics of JavaScript (remember jQuery?) simply don’t understand the functional side of JavaScript. Cmon guys, the class is simply a syntactic sugar.

OK, enough grumbling for now.

I want to share a simple trick I’ve started using recently while developing one of my personal projects. It’s very simple. It abstracts away the object-oriented nature of the component, so I don’t have to deal with this in my code.

I called it nothis.

nothis is a function that takes an object and builds a stateful component out of it. You can pass to it the initial state, the lifecycle hooks, and the render method.

import React from 'react'
// ... other imports

import nothis from '../shared/nothis'

export default nothis({
  willMount,
  render,
})

function render({ state: { loading, card }, setState }) {
  return (
    <div className="card">
      { loading && <Spinner /> }
      { !loading && (
        <div>
          <span>{ card.name }</span>
          <a onClick={remove}>Remove</a>
        </div>
      ) }
      <span>{ card.name }</span>
    </div>
  )

  async function remove() {
    setState({ loading: true })
    await remove(card.id)
    setState({ loading: false, card: undefined})
  }
}

async function willMount({ setState }) {
  setState({ loading: true })
  const card = await fetchCard()
  setState({ card, loading: false })
}

You can see that when you need a callback, you can put the callback function within the render function itself.

Yes, you can have functions within functions. And yes, you can define functions after the return statement. See hoisting. And this is actually a great way to structure your code, cause it adds up to readability no matter what your linter says.

Now this (or something like this) is what I wish the API for React components was like from the very beginning. No stupid classes. Because React is functional in its nature.

I could have actually stopped there because I wanted to talk about the API and not the implementation. But I know some of you may be curious, so let’s actually talk about how nothis could look like.

The implementation takes advantage of the fact that class in JavaScript in its core is a simple function (more or less). And like every other function, it doesn’t have to be statically defined. You can generate it on the fly.

export default function ({ state, render, willMount }) {
  return class extends React.Component {
    constructor(props) {
      super(props)
      this.state = state || {}
    }

    async componentWillMount() {
      if (!willMount) return
      await willMount(ctx(this))
    }

    render() {
      return render(ctx(this))
    }
  }
}

function ctx(th) {
  return {
    props: th.props,
    state: th.state,
    setState: th.setState.bind(th),
  }
}

I’m sure it’s not the only way (and most certainly not the most efficient) way to implement it, but it’s very simple. We simply create the class on the fly and pass the “context” (ctx function) to the provided functions.

And that’s it.

 
1
Kudos
 
1
Kudos

Now read this

Essential Form Interactions

There are several important features every form should have, be it a simple login form, or a complicated multi page questionnaire. Most of them are really easy to implement but are often neglected. On the other side as from user... Continue →