A React-based implementation of the ideas of "Functional Observer REST" - replacing Redux
JavaScript
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
docs
examples
lib
src
.babelrc
.gitignore
LICENSE
README.md
package-lock.json
package.json
webpack.config.js

README.md

Forest

A React-based, Javascript implementation of the ideas of FOREST - "Functional Observer REST" - replacing Redux, Reselect and Redux Saga in your dynamic Web pages.

"Functional Observer REST?"

Yes, Functional Observer is a programming model that is entirely based around state and pure functional transformation from state to state.

There are no actions, events, messages, commands, calls, queues, streams, etc. in Functional Observer, just state observing other state and updating itself. The React component state observes other states around it:

ComponentState =

  ƒ(

    ComponentState,
    UserEnteredState,
    PeerComponentStates,
    RemoteJSONAPIStates

  )

FOREST (Functional Observer REST) just adds HTTP to that model, via the RemoteJSONAPIStates above.

This simple model replaces all the complexity of Redux, Reselect and Redux Saga with a highly declarative, visible set of state transformation functions, per component.

"Sounds very academic! Show me the code!!"

OK, here's a minimal example:

/* React render reading from `state` for this object's state */
/* Also has `userState` for declaring widgets without actions or events! */
/* If the first, name arg matches a `state` fieldname, it reads from there */
function renderMin(state, userState){
  return (
    <div>
      <hr/>
      {userState.textField('message')}
      <br/><br/>
      <span>Count: {state('counter')}</span>&nbsp;&nbsp;&nbsp;
      {userState.button('inc', {label: 'increment'})}
      <br/><br/><hr/><br/>
    </div>);
}

/* Mapping to above render function from a string matching */
/* the `is:` property of the backing state object. */
const renderers = {
  'minimal': renderMin
};

/* Initial list of state objects */
/* The first one is taken to be the 'top' state object. */
forest.storeObjects(
  [{ Evaluator: evalMin,
     is: 'minimal',
     message: 'Hello World!',
     counter: 17
  }],
  renderers
);

/* Where all the domain logic goes: pure functional */
/* transform/reduction of visible state to new component state: */
/* `state = f(state, state.userState)` */
function evalMin(state){
  const incrementPushed  = !state('inc') && state('userState.inc');
  return Object.assign({},
    true            && { message: state('userState.message').toLowerCase() },
    incrementPushed && { counter: state('counter') + 1 },
    true            && { inc: state('userState.inc') }
  );
}

An object reads the states around it (userState above, but also peer states and API states) 'through' its own state, because in Forest, the dot . can jump across to those other objects to read (and then observe) them.

For example, above, state('userState.message') reads userState, which is simply the UID of the userState object, then fetches that object and reads its message property. It then continues to be notified of changes to that object.

You can also discover peer component and remote API state in the same way (e.g. state('selectorPeerComponent.choice') or state('referenceData.choicelist')).

The fact that Forest only uses state instead of actions or events means that detecting change is done through comparison to previous state. For example, the expression above incrementPushed = !state('inc') && state('userState.inc') detects that the userState inc button is true (down) while the known state was false (up). The line inc: state('userState.inc') then records the latest state for the next time around.

"Why is that better?"

This consistent object graph traversal for all state (component, current user input, peer component, remote data), combined with the pure functional state transformation makes the domain logic simple and powerful.

It puts a lot of power into small state reducer or transformation functions and expressions - one single dot . can give a component access to all the state around it, locally and remotely, and subscribe it to that state so that it's notified if it changes. All the interactive logic is held in pure functions.

Credit

Thanks to my Tes colleague, Federico 'framp' Rampazzo, for the inspiration, example and base code from Storry!

Similar

The Eve Language is one of the closest examples I've found of a similar approach. The Red Language also has a number of aspects in common.