How to create a store using pure functions

Cristian Salcescu
Jan 29 · 4 min read

Check my Discover Functional JavaScript and Functional React books.

Photo by Ugur Akdemir on Unsplash

Pure functions produce the same output value, given the same input. They have no side-effects, and are easier to read, understand and test.

Given all this, I would like to create a store that hides the state but at the same time uses pure functions.

Immutability

An immutable value is a value that, once created, cannot be changed.

Immutable.js provides immutable data structures like List. An immutable data structure will create a new data structure at each action.

Consider the next code:

import { List } from "immutable";const list = List();
const newList = list.push(1);

push() creates a new list that has the new element. It doesn’t modify the existing list.

delete() returns a new List where the element at the specified index was removed.

The List data structure offers a nice interface for working with lists in an immutable way, so I will use it as the state value.

The Store

State is data that can change. The store hides that state data and offers a public set of methods for working with it.

I would like to create a book store with the add(), remove() and getBy() methods.

I want all these functions to be pure functions.

There will be two kind of pure functions used by the store:

  • functions that will read and filter the state. I will call them getters.
  • functions that will modify the state. I will call them setters.

Both these kinds of functions will take the state as their first parameter.

Let’s write the store using pure functions.

import { List } from "immutable";
import partial from "lodash/partial";
import matchesProperty from "lodash/matchesProperty";//setters
function add(books, book) {
  return books.push(book);
}function remove(books, book) {
  const index = books.findIndex(matchesProperty("id", book.id));
  return books.delete(index);
}//getters
function queryContainsBook(query, book) {
  if (query && query.text) {
    return book.title.includes(query.text);
  }
  return true;
}function getBy(books, query) {
  return books.filter(partial(queryContainsBook, query)).toArray();
}

The Library

How can this be achieved?

We have seen the answer in Redux. We write pure functions and let the library create the store and apply the pure functions.

Here is how I would like to use the library to defined the store:

import { List } from "immutable";
import Store from "./Store-toolbox";//setters
function add(books, book) {}
function remove(books, book) {}//getters
function getBy(books, query) {}export default Store({
  state: List(),
  setters: { add, remove },
  getters: { getBy }
});

Let’s build this micro-library that creates the store based on the pure getters and setters.

All public getters and setters will be decorated and will receive state as their first parameter.

  • The return value from getters will be returned to the caller functions.
  • The returned value from stetters will be used to change the state. The caller functions will not receive the new state.
function decorateMethods(obj, decorator) {
  let newObject = { ...obj };  Object.keys(newObject).forEach(function decorateMethod(fnName) {
    if (typeof newObject[fnName] === "function") {
      newObject[fnName] = decorator(newObject[fnName]);
    }
  });  return newObject;
}function Store(storeConfig) {
  return function() {
    let state = storeConfig.state;    function setter(fn) {
      return function(...args) {
        state = fn(state, ...args);
      };
    }    function getter(fn) {
      return function(...args) {
        return fn(state, ...args);
      };
    }    return Object.freeze({
      ...decorateMethods(storeConfig.getters, getter),
      ...decorateMethods(storeConfig.setters, setter)
    });
  };
}export default Store;

Store() creates a function that returns an object that encapsulates state.

Let’s create and use the bookStore object:

import BookStore from "./BookStore";const bookStore = BookStore();
bookStore.add({ id: 1, title: "How JavaScript Works" });

When calling bookStore.add({}), the setter decorator will call the add() pure setter function with current state as the first parameter and the new book as the second parameter. Then, the setter decorator will use the result to change the value of state.

The Object

It only exposes three methods, all the other pure functions are private.

The public interface of the object can’t be modified from the outside.

The state is hidden. The clients using the bookStore object can access the state only through the public methods.

Conclusion

We can create a store that hides state and uses pure functions with the help of a library.

We can write only the pure functions and let the library apply them and do the mutation.

You can check out the sample code on codesandbox.io.

Discover Functional JavaScript was named one of the best new Functional Programming ebooks by BookAuthority!

For more on applying functional techniques to React take a look at Functional React.

You can find me on Twitter.

Cristian Salcescu

Written by

Author of Discover Functional JavaScript and Functional React. Enthusiastic about sharing ideas.