A VERY functional approach to React token-based authentication.

Buckle in folks cause this is going to be a long one.

This post covers the intersection of authentication, Redux, Ramda, Reselect, and Recompose, all in the context of protecting our authenticated routes in a React app. We won’t be diving too deep into any library in particular, but we’ll discuss how to use them in tandem to run the world.

First let’s do a brief primer on each of our angles of attack.

Authentication

This will NOT be the most secure web app in the world. We will be flying by the large assumption that if we are currently storing a token in Redux, we are authenticated. If token is null , we are unauthenticated.

What is Redux? This is far too deep a rabbit hole to dig into right now; however, let’s rely on the fact that Redux is where we store all the state (data) for our React app.

Components can read from and write to our Redux store (imagine a big, shared JavaScript object) and this is where we will store our token . Our Redux store will be shaped like this:

// Describing the type of our Redux store using `flow` type ReduxStoreType = {

auth: {

token: ?string

}

}

For demonstration purposes, our Redux store will have one branch (auth) which has a token that can be a string or null .

If you’re familiar with Underscore.js or Lodash, you’ll love Ramda even more. Ramda applies immutability and functional approaches to their implementation of these generic “toolbelt” functions.

One of the great advantages of Ramda is that their functions are automatically curried. Currying provides huge benefits in functional programming which relies heavily on the principle of partial application. Here’s an example without going too much into theory:

// Ramda example const R = require('ramda') const DATA = [1, 2, 3, 4, 5] // Full application

const uncurriedExample = R.indexOf(3, DATA) // 2 // Partial application

const curriedExample = R.indexOf(3)(Data) // 2 // Or if you want to create a function beforehand // Create function to be used later

const indexOf3 = R.indexOf(3) const anotherCurriedExample = indexOf3(DATA) // 2

It may not be obvious, especially if you come from an OOP background, but currying lays the groundwork for configurable factory functions and other functional programming constructs.

Reselect is a library for computing derived data with memoized selectors.

What 🤔?

Skipping over memoization, Reselect pairs excellently with Redux to help you grab the exact data you want out of a branch of your redux store. This concept can be hard to understand, specifically if you haven’t dealt with the problem is solves. Here’s an example:

// Example Redux Store using `flow` type type ReduxStoreType = {

friends: {

activeIndex: number,

data: Array<Friend>,

}

}

Now, if you just wanted to see your activeFriend = friends.data[activeIndex] many developers would access this data directly inside of a render function. This couples your component directly to the structure of your data which isn’t a maintainable practice in large applications and minimizes code reuse.

Here’s an example of this BAD practice:

// BAD EXAMPLE - GreatFriend.js import React, { Component } from 'react'

import { connect } from 'react-redux' class GreatFriend extends Component {

render() {

const { activeIndex, data } = this.props

const activeFriend = data[activeFriend] return <h1>{activeFriend.firstName}</h1>

}

} const mapStateToProps = state => ({

friends: state.friends,

}) export default connect(mapStateToProps)(GreatFriend)

This is bad for a couple reasons. First, like we mentioned above, GreatFriend is directly responsible for finding the activeFriend . We’re directly exposing data-structure details to this component, which is not it’s direct responsibility.

Another issue here is that whenever anything inside the friends key of the Redux store changes, this component will re-render, leading to poor performance.

Now, here’s an example of how we can use Reselect to create a memoized selector to grab just our activeFriend eliminating re-renders and decoupling our component and data:

// GOOD EXAMPLE - GreatFriend.js import React from 'react'

import { connect } from 'react-redux'

import { createSelector } from 'reselect' // Selectors

const friends = state => state.friends // Base selector to get the activeIndex from friends

const getActiveIndex = createSelector(

friends,

_ => _.activeIndex

) // Base selector to get the data from friends

const getData = createSelector(

friends,

_ => _.data

) // Compose base selectors to get activeFriend

const getActiveFriend = createSelector(

[getData, getActiveIndex],

(data, activeIndex) => data[activeIndex]

) // Component

const GreatFriend = props => <h1>{props.activeFriend.firstName}</h1> const withRedux = connect(state => ({

activeFriend: getActiveFriend(state)

})) export default withRedux(GreatFriend)

This may look convoluted now, but with a couple selector factory functions and some code organization, we achieve a very clean architecture that separates Redux, data access, and rendering.

Recompose is another functional “toolbelt” library that abstracts common React functionality implementation details into pre-packaged higher-order components (HOCs).

HOCs are a very interesting and powerful pattern to enhance dumb components with new, reusable, composable functionality. Recompose exposes some simple and some complex HOCs for developers.

Here’s a quick example of how we can move in-component PropTypes declarations from within a component to an enhanced HOC.

// In-component PropTypes import React, { Component } from 'react'

import PropTypes from 'prop-types' class Example extends Component {

static propTypes = {

name: PropTypes.string.isRequired,

} render() {

return <h1>{this.props.name}</h1>

}

}

Versus, the functional approach enabled by Recompose:

// Enhancer-based PropTypes import React from 'react'

import PropTypes from 'prop-types'

import { compose, setPropTypes } from 'recompose' const Example = props => <h1>{this.props.name}</h1> // Enhancers

const withPropTypes = setPropTypes({

name: PropTypes.name.isRequired,

}) export default compose(withPropTypes)(Example)

In the second example here, we’re able to write a dumb functional component (not class-based) and compose new functionality onto it.

This is a VERY simple, contrived example; however, hopefully it conveys the point. We’ll dive a little deeper in a sec.