forest-react

Simple, powerful alternative to Redux, Reselect, Sagas, etc

Downloads in past

Stats

StarsIssuesVersionUpdatedCreatedSize
forest-react
130.0.886 years ago6 years agoMinified + gzip package size for forest-react in KB

Readme

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 `this` for declaring widgets without actions or events! */
/* If the first, name arg matches a `state` fieldname, it reads from there */
function render(object){
  return (
    <div>
      <hr/>
      {this.textField('message')}
      <br/><br/>
      <span>Count: {object('counter')}</span>   
      {this.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.user-state)` */
function evalMin(state){
  const incrementPushed  = !state('inc') && state('user-state.inc');
  return Object.assign({},
    true            && { message: state('user-state.message').toLowerCase() },
    incrementPushed && { counter: state('counter') + 1 },
    true            && { inc: state('user-state.inc') }
  );
}

An object reads the states around it (user-state 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('user-state.message') reads user-state, which is simply the UID of the user-state 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('user-state.inc') detects that the user-state inc button is true (down) while the known state was false (up). The line inc: state('user-state.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.