elfi

Using elfi

This guide details the basic concepts of elfi and its usage.

For information about how to use elfi in a React environment, please take a look at the elfi/react docs

Basic concepts

elfi allows you to create a store which holds the whole state of your application. The state is updated by dispatching functions that return a new state based on the previous ones. Such functions are called changes, and they should be pure functions (i.e. having no side effects).

If you are familiar with Flux or Redux this might sound familiar to you, but it strives to remain simple by eliminating most of the boilerplate that you would expect to find with them. There are no dispatchers, no reducers, no actions and no action creators in elfi, only simple functions.

Finally, the store can accept subscribers which are also functions and which are called when a state change occurs.

Creating a store

Creating your elfi store is done by importing createStore and calling it with an initial state.

import { createStore } from "elfi"

const store = createStore(1)

In the example above, the state of our application is a number. This is perfectly valid and elfi enforces no specific type for the internal state of the store.

You can query for the current state of the store using getState:

store.getState() // => 1

Dispatching changes

As mentioned previously, a change is a function that returns a new state based on the current state of the store.

Continuing our integer store example, we can write an increment change like this:

function increment(state) {
  return state + 1
}

Such a change can be dispatched using store.dispatch:

store.dispatch(increment)
store.getState() // => 2

Any extraneous arguments passed to dispatched will be passed to the change as well. This allows us to write and add change like this:

function add(state, n) {
  return state + n
}

store.dispatch(add, 40)
store.getState() // => 42

Listening for changes

You can add a subscriber by using store.subscribe. A subscriber is a function that takes two arguments: the old state and the new state. All subscribers of the store are called when a change occurs, and only if this change actually modifies the internal state of the store.

store.subscribe((oldState, newState) => console.log(newState))
store.dispatch(increment) // logs 43
store.dispatch(x => x) // does not log anything since state is unchanged

store.subscribe returns a function that can be used to stop listening for changes:

const unsubscribe = store.subscribe(mySubscriber)
// do things
unsubscribe()

Middleware

Middleware is a thin layer that allows you to customize the behavior of the store by hooking into the dispatching process.

A middleware is a function (again!) that takes at least 3 arguments:

Here’s an example of a simple logging middleware:

function loggerMiddleware(next, oldState, change, ...args) {
  const newState = next(oldState, change, ...args)
  console.log(change.name, oldState, newState)
  return newState
}

You define what middleware you want to use at store creation time. createStore takes a second argument which is an array of middleware functions to use:

const store = createStore(1, [loggerMiddleware])

Calling next chains to the next middleware piece, or to the internal dispatching mechanism. You should always return a valid state in your middleware or the internal state of your store will take the value of undefined.

elfi ships with some builtin middleware for common tasks, you can get more information about it in the middleware documentation.

Fork me on GitHub