Functional Components with React stateless functions and Ramda

July 04, 2016 0 Comments react, functional

Functional Components with React stateless functions and Ramda

Functional Components with React stateless functions and Ramda

What you need to know:

  • A bit of functional programming (compose, curry, lenses)

  • A bit of React
  • A bit of ES6

Note: I will use JSX notation to simplify the code samples.

What is a React stateless function?

Usually you may define a React component as follows:

Or using the ES6 class syntax:

Or using a plain JavaScript function! Did you know?

A quote from the React documentation:

This simplified component API is intended for components that are pure functions of their props. These components must not retain internal state, do not have backing instances, and do not have the component lifecycle methods. They are pure functional transforms of their input, with zero boilerplate. However, you may still specify .propTypes and .defaultProps by setting them as properties on the function, just as you would set them on an ES6 class.

And again:

In an ideal world, most of your components would be stateless functions because in the future we'll also be able to make performance optimizations specific to these components by avoiding unnecessary checks and memory allocations. This is the recommended pattern, when possible.

Pretty interesting right?

The React community seems to be more focused about the Class or the classic createClass approach, let's do some experiments with stateless components.

App container

First thing first let's build an App container component as a function that accept an app state object:

Then define a render method as a property of the App function:

Wait a moment! Why do we need a curried render method that leverage on ReactDOM.render? And why are render method parameters (node and props) in a different order? For now the only explanation needed here is that, since we are using stateless components that are pure functions to their props, state must be managed elsewhere. In other words state must be managed externally and passed down to the components as props. Let's see a concrete example by building a timer.

Stateless Timer component

A simple timer component that will accept only one prop secondsElapsed:

Add it to App:

Finally create a main.js file and start rendering!

Before explaining what's going on here, let me say that mutating the appState like I did in appState.secondElapsed++ make me feel very bad but later on we will write some kind of reducing function for that kind of stuff.

So what we can understand now is that render is just syntactic sugar for making continuous re-rendering of a component with new props, the line:

const render = App.render(document.getElementById('app'));

Will return a new function with this signature:

(props) => ReactDOM.render(...)

No rocket science here! Any time we want to react to a change of the state we can re-render as we did with setInterval:

setInterval(() => {
appState.secondsElapsed++;
render(appState);
}, 1000);

Each second secondsElapsed property gets incremented and render is called within the updated appState.

Now we will implement a reducing function in a Redux style for incrementing the value of secondsElapsed. A reducing function must not mutate the current state and at a very simple implementation has the following signature:

currentState -> newState

And here we have our incSecondsElapsed reducing function implemented using Lenses from Ramda:

First of all we created a Lens:

const secondsElapsedLens = R.lensProp('secondsElapsed');

In simple words a lens is a way to keep the focus on a given property without specifying on which object, they are cool because of their reusable nature. So if we apply a Lens against an object we can:

R.view(secondsElapsedLens, { secondsElapsed: 10 });  //=> 10
R.set(secondsElapsedLens, 11, { secondsElapsed: 10 });  //=> 11
R.over(secondsElapsedLens, R.inc, { secondsElapsed: 10 });  //=> 11

Our incSecondsElapsed reducer is the result of the partial application of R.over. This line:

const incSecondsElapsed = R.over(secondsElapsedLens, R.inc);

Will return a new function that, once called within our appState, will apply R.inc on the lensed prop secondsElapsed.

It is worth noting that Ramda never mutate objects, we still have to do the dirty job:

appState = incSecondsElapsed(appState);

If you want to support undo/redo you could easily implement by yourself a history array where to push new app states or you could use Redux.

Until now we have seen few stuff about currying and lenses, let's experiment with compose.

Functional Components with React stateless functions and Ramda


Tag cloud