blog.jakoblind.no

How to use recompose withState to add state to functional components

Recompose is a library with utility functions for functional React components. We are going to take a closer look at how we can use recompose withState function to add state to a functional component.

Is recompose legit?

Recompose is written by one of the co-authors of Redux, acdlite. It has over 4k stars on Github (I guess that adds some legitimacy). And the library encourages composition which is officially recommended in favor of inheritance.

So it seems legit to me. I use it in production.

The use case

For me, a common development pattern is working “front end first”. That means I start by taking a look at the mockups that the designer (or me sometimes) has sketched up. I then start coding the JSX and CSS and I work mainly with functional components.

Eventually I come to a point where I need state.

So you have a functional component

Let’s look at an example. Imagine you are working on a component that looks like this:

const ExpandButton = () => (
    <div className="expand-button">
        <div className="arrow" >
    </div>
);

It’s a button that has a down arrow in it. The rendered HTML looks like this: (CSS is not included in this post)

recompose withState for expand button

A nice looking static button!

Adding state with recompose withState

Now you realize you need that expand button to actually expand the component when the user clicks on it. So now you need state. Because this is a simple UI thing you decide you don’t need that piece of state in Redux (maybe you read my post about it being fine to have parts of your state in React components).

To add state with recompose, we need to do two things: define the state, and “connect” the state to our component.

1. Define state

One of the utility functions that Recompose provides is withState. The recompose withState function call looks like this:

const enhanceWithExpandedState = withState("expanded", "setExpanded", false)
  • The first argument to recompose withState is the name of the state variable.
  • The second argument is the name of the setter function.
  • And the third argument is the default value of the state variable.
  • A function is returned that we call enhanceWithExpandedState in this case. We will use this function when connecting the state to our component.

2. Connect the state to component

Then you can use that state with your component like this:

const ExpandButton = enhanceWithExpandedState(({ expanded, setExpanded }) => (
  <div className="expand-button" onClick={() => setExpanded(!expanded)}>
    {expanded ? (
      <div className="arrow-down"></div>
    ) : (
      <div className="arrow-up"></div>
    )}
  </div>
))

Now our component receives two props, expanded and setExpanded, which we defined previously.

But isn’t state and props different things?

Functional components don’t support the concept of state, it only supports props. Therefore, we need some place to store the state and we use recompose for that. The only way to send data from the state to a functional component is with props. That’s why we receive the state via props.

Comparing with using a class

Just for comparison let’s take a look how it would look to implement this with a class:

class ExpandButtonContainer extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      expanded: false,
    }

    this.setExpanded = this.setExpanded.bind(this)
  }
  setExpanded(expanded) {
    this.setState({ expanded })
  }
  render() {
    return (
      <ExpandButton
        expanded={this.state.expanded}
        setExpanded={this.setExpanded}
      />
    )
  }
}

It’s more code.

So is using a class out of the question?

Does this mean we should only forever do functional components? It depends on the requirements of your application.

These are reasons not to use recompose and stick to classes:

  • Recompose is an extra library which adds to the size of your bundle
  • It’s an extra API to learn for you and all your teammates (and all future teammates)
  • Less code does not always mean more readable code.

These are reasons to use recompose instead of classes:

  • Concise syntax.
  • Easy to move state to other components.
  • Possibility to reuse state definitions.

Next step after recompose withState

You can do much more things with recompose. Life cycle methods, composing higher order components, etc. It’s a nice package and I suggest you check it out more!

Do you want to learn more about recompose and other advanced concepts? Follow me on Twitter to get real-time updates with tips, insights, and things I build in the frontend ecosystem.


tags: