Update Your Application with more FRP

Up until this point, we've been dealing with messages as typical redux-style actions, being objects that have a type field along with other relevant values that the reducer can use. This is called the reducer pattern, but that's not the only approach you can use to update your applications.

The one important thing to remember is that Ferp doesn't care about what a message is, or what your update function does, as long as it can result in a tuple of your new state and a follow-up effect.

Update with unions

If you're familiar with Elm, you've probably seen actions declared as a union type, which looks something like this:

type Actions
= Foo String Number
| Bar Number Number
| Baz Boolean

With a shameless plug, you can use a similar pattern using js-enumerize (other union/enumeration libraries may also work, too).

import { app, effects } from 'ferp';
import enumerize, { Any } from '@mrbarrysoftware/js-enumerize';
const Actions = enumerize({
Foo: [String, Number],
Bar: [Number, Number],
Baz: [Boolean],
});
app({
init: [initialState, Actions.Foo('test', 123)],
update: (message, state) => Actions.caseOf({
Foo: (name, age) => [
{ ...state, people: state.people.concat({ name, age }) },
effects.none(),
],
// ... Handle other actions here...or not, and let _ catch them.
_: () => [state, effects.none()],
}),
});

This is still pretty close to the switch statement, but has a more functional feel if you prefer to keep with redux-style action messages.

Update with functions

More towards the side of Hyperapp, there shouldn't be anything preventing you from passing function as messages. Following the above Foo, Bar, and Baz actions, let's see what those could look like as functions:

import { effects } from 'ferp';
const Foo = (name, age) => state => [
{ ...state, people: state.people.concat({ name, age}) },
effects.none(),
];
const Bar = (currentNumber, total) => state => [
{ ...state, percentage: Math.round(currentNumber / total * 100) },
effects.none(),
];
const Baz = (toggle) => state => [
{ ...state, toggle },
effects.none(),
];
app({
init: [initialState, Foo('test', 123),
update: (message, state) => message(state),
});

This feels a lot more modular than updating with unions or switch statements, and makes it easier to test individual actions without having to use the update method from the app definition.