Minimal unidirectional global state management library (based on basic functional programming concepts).
Description • Demo • Getting started • Minimal example • Concepts • Example with Ramdajs
Ortum means "the rise" or "origin" which resonates with the functionality of the library with regards to functional state management
Ortum is a small framework agnostic, unidirectional state management library.
It's inspired by the work of André Staltz' on
Profunctor State Optics
Ortum allows your features to be independent from the global state while having
the ability to use the global state.
By using this, your features are decoupled from their environment, thus easy to
test.
A demo can be found here: https://stackblitz.com/edit/ortum-counter?file=src/counter.ts
Install into your project:
$ npm install --save ortumconst { SimpleStateContainer, useProfunctor } = require('ortum');
const [appProf, onStateChange] = useProfunctor(
new SimpleStateContainer({ counter: 0 }),
);
onStateChange((state) => console.log(state));
console.log(appProf.getState()); // { counter: 0 }
appProf.setState(
({ counter, ...state }) => ({...state, counter: counter + 1 })
);
const counterProf = appProf.promap(
(state) => state.counter,
(counter, state) => ({ ...state, counter })
);
console.log(authorsProf.getState()); // 2Don't get scared away by the functional jargon that's being used. They are just terms for patterns that you already use.
Something on which you can call map over with a single function to transform
each element.
For example an array:
[1,2,3,4].map(x => x * x) // [2,4,9,16]Something on which you can call promap over, however, you can pass two
functions:
- a map (
x => x * 2), times two - a 'reverse'-map (
x => x / 2), divide by two
This is some pseudo-code explaining what happens:
const prof1 = new Profunctor([1,2,3,4]);
const prof2 = prof1.promap(x => x * 2, x => x / 2)
prof2.get() // [2,4,6,8]
// ^ the first function applied on the array of prof1
prof2.set(([first, ...rest]) => [10, ...rest])
// ^ the second function applied on the array of prof2
prof2.get() // [10,4,6,8]
prof1.get() // [5,2,3,4]
// this code is for explanatory purpose, not an example of OrtumThis is what happens:
- A Profunctor
prof1holds some state[1,2,3,4]and is 'promappable'. - When promapping you get a new Profunctor
y. prof2now holdsprof1.map(x => x * 2)asget-function and a reversed version asset-function
The same concept can be applied on an object:
const prof1 = new Profunctor({ counter: 0, title: 'Profunctors' });
const prof2 = prof1.promap(
obj => obj.counter,
(counter, obj) => ({ ...obj, counter })
)
// this code is for explanatory purpose, not an example of OrtumYou guessed it; prof2 holds only the value of counter: 0. And the
set-function, puts it back in the prof1-state.
Ramdajs is a nice functional library that can remove a lot of boilerplating from
your promap-functions.
Because the signatures of Ortum's promap-functions have a 'data-last'
approach, it works fluently with other functional libraries that use currying.
import * as R from 'ramda';
import { useProfunctor, SimpleStateContainer } from 'ortum';
const appProf = useProfunctor(
new SimpleStateContainer({ foo: { bar: { baz: 'value' } } })
)
const bazLens = R.lensProp(['foo','bar','baz']);
const bazProf = appProf.promap(
R.view(bazLens),
R.set(bazLens)
)
bazProf.getState() // 'value'
bazProf.setState('another value')
bazProf.getState() // 'another value'Instead of making the two map/unmap functions manually, we can use lenses with
view and get from Ramda to simplify our code. This would be the alternative:
appProf.promap(
state => state.foo.bar.baz,
(baz, state) => {
...state,
foo: {
...state.foo,
bar: {
...state.foo.bar,
baz
}
}
}
)