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:
- The next middleware function to call,
- The current store state,
- The change function that is being dispatched,
- And any extra arguments to pass to the change.
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.