Building your own Redux clone using the recompose library.

A few weeks ago, I spent some time playing around with the recompose library.

Recompose is a set of helpers that create Higher Order Components (HOC) for React and add encapsulate behaviours that you can apply to any React component.

While making stuff on codesandbox (This tool is awesome you should use it!), I decided that I would build a tiny redux clone with what I had just learnt.

What is a Higher-order component ?

In functional programming, we call a « Higher Order Function » a function that takes functions as parameters and/or returns a new function.

// Here is our higher order function

const andThen = (fn) => (promise) => promise.then(fn) // Here is a simple function (or first-order function)

const logger = (x) => console.log(x) // andThen(logger) returns a new function that expects a promise !

const logPromise = andThen(logger)

Let’s use our logPromise function :

const p1 = Promise.resolve(42) logPromise(p1)

// logs '42'

Now you now what is a higher order function. I’m sure that you’ve already used it before. These functions are everywhere in Javascript. (Hint hint Array.forEach 😉 😉)

By using the same analogy, a « Higher Order Component » (or HOC) is a function that takes React components as parameters and/or returns a React component.

Now that we know what is a HOC, let’s use Recompose on a simple example.

Let’s try Recompose

Recompose gives us helper functions that let us create HOCs.

These helpers can be composed into a and add behaviours to any react components. They accept a base component and return a new component with additional functionality.

My main takeaway with recompose is this :

What if you could build react components without relying on ‘this’ ?

Let’s have a look at an example from the docs :

const enhance = withState('counter', 'setCounter', 0)

const Counter = enhance(({ counter, setCounter }) =>

<div>

Count: {counter}

<button onClick={() => setCounter(n => n + 1)}>Increment</button>

<button onClick={() => setCounter(n => n - 1)}>Decrement</button>

</div>

)

Here the withState HOC takes 3 params :

the key 'counter' is the name of the field where you will store your state, much like you’d do on class components with this.state.counter the key 'setCounter' is the name of the function that will update the state counter with a new value, like this.setState(oldState => ({counter: oldState + 1 })) the initial value of 'counter'

The value of the state counter and the state updater setCounter will be passed down as props to your component wrapped by the HOC enhance

It’s like using setState but in a functional react component! This is huge!

And there are more helpers available like mapProps, defaultProps , withContext , withReducer , withHandler , lifecycle (to hook to React lifecycle callbacks) … (and many more!)

If you want to read more about Recompose, I suggest you this excellent article by @sharifsbeat : Why The Hipsters Recompose Everything

Recomposing Redux

So like I said in the beginning of this post, I was playing around with Recompose and I thought « Hey this recompose thing is tight! I’ll try to build Redux clone with it »

Let me show you now what I made 😉

createStore

In Redux, the createStore function creates a redux store.

It takes an initialState and a reducer and returns a new store :

const createStore = (initialState, reducer) => {

let state = initialState

return {

getState: () => state,

subscribe: cb => {},

dispatch: action => {

state = reducer(state, action)

}

}

}

Here’s a basic redux store.

Now let’s add an event emitter to refresh all connected components when the state changes.

To do so, I used mitt from @_developit

import mitt from 'mitt' const createStore = (initialState, reducer) => {

// a new event emitter instance

const emitter = mitt()

let state = initialState

return {

getState: () => state,

subscribe: cb => {

// subscribe to "update" event

emitter.on('update', cb)

return () => emitter.off('update', cb)

},

dispatch: action => {

state = reducer(state, action)

// trigger an "update" event

emitter.emit('update', state)

}

}

}

That’s it! We now have a createStore function ready!

The Provider component

The Provider component in Redux takes the redux store in its props and pass it to all connected components through the Context API of React.

We will need to use the withContext function from recompose :

import React from 'react'

import { withContext } from 'recompose' import PropTypes from 'prop-types' // defining contextTypes is mandatory

const contextTypes = { store: PropTypes.object } const Context = withContext(contextTypes, props => ({

store: props.store

})) const Provider = Context(({ children }) => <div>{children}</div>)

We take the redux store from the props of Provider and we put it under the 'store' key of the context object

react-redux connect

The connect function from 'react-redux' takes 2 functions ( mapStateToProps and mapDispatchToProps ) and returns a HOC .

This HOC takes care of :

getting the store from the context passing down the state from the store to the component subscribing to store changes

For each of these steps, we will use a dedicated recompose helper :

getting the store from the context -> getContext passing down the state from the store to the component -> withState subscribing to store changes -> lifecyle

Now let’s compose them using the compose function provided in 'recompose' !

import React from 'react'

import { compose, withState, getContext, lifecycle } from 'recompose' import PropTypes from 'prop-types' // defining contextTypes is mandatory

const contextTypes = { store: PropTypes.object } const enhance = compose(

// 1. getting the store from the context

getContext(contextTypes), // 2. passing down the state from the store to the component

// using the 'state' prop

withState('state', 'setStoreState', {}), // 3. subscribing to store changes

lifecycle({

componentDidMount() {

const { store, setStoreState } = this.props

setStoreState(store.getState())

this.unsub = store.subscribe(newState => {

setStoreState(newState)

})

},

componentWillUnmount() {

if (this.unsub) {

this.unsub()

this.unsub = null

}

}

})

)

Now we can create our connect function :

const connect = (mapStateToProps, mapDispatchToProps) => Component =>

enhance(({ store, state, ...ownProps }) => {

const derivedState = mapStateToProps

? mapStateToProps(state, ownProps)

: state

const dispatchers = mapDispatchToProps

? mapDispatchToProps(store.dispatch, ownProps)

: store.dispatch

return <Component {...derivedState} {...dispatchers} {...ownProps} />

})

Done!

We have everything in place now in our redux clone.

Let’s not forget that the redux codebase has many more checks and optimizations than this simple implementation (for example, to prevent unnecessary re-renders when calling store.dispatch() )

Using our « recomposed » redux

Let’s create a reducer, an initial state and a store :

const reducer = (state, action) => {

const value = action.value || 1

switch (action.type) {

case 'INCREMENT':

return { ...state, count: state.count + value }

case 'DECREMENT':

return { ...state, count: state.count - value }

default:

return state

}

} const initialState = { count: 0 }

const store = createStore(initialState, reducer)

Here are our mapStateToProps and mapDispatchToProps functions :

const mapStateToProps = (state, ownProps) => ({

num: state.count

}) const mapDispatchToProps = (dispatch, ownProps) => ({

increment: () => dispatch({ type: 'INCREMENT' }),

increment4: () => dispatch({ type: 'INCREMENT', value: 4 }),

decrement: () => dispatch({ type: 'DECREMENT' })

})

And our App component that we will wrap with connect(mapStateToProps, mapDispatchToProps) :

const App = ({ title, increment, increment4, decrement, num }) => (

<div>

<h2>{title}</h2>

<p>Count : {num}</p>

<button onClick={increment}>+1</button>

<button onClick={decrement}>-1</button>

<button onClick={increment4}>+4</button>

</div>

) const CounterApp = connect(mapStateToProps, mapDispatchToProps)(App)

Let’s boot everything up with the Provider component we created before

ReactDOM.render(

<Provider store={store}>

<CounterApp title="Counter" />

</Provider>,

document.getElementById('root')

)

As you can see, we use it almost exactly like the original redux!

(I said almost because I did not create a combineReducers function and split the store in sub-stores like redux does with combineReducers - maybe I'll update this article if I do it)

The end result is available here 😎 :