monarc

An advanced and extensible store for React applications, MONARC's Obviously Not A Redux Clone


Project maintained by cvlmtg Hosted on GitHub Pages — Theme by mattgraham

Using MONARC 🦋

Like any state management solution, we have different bits to create before we can use our application. This brief document assumes you already have used some state management frameworks or that you are at least familiar with the underlying concepts.

Reducer

This is the equivalent of Flux’s or Redux’s reducer concept, i.e. a function that accepts a state and an action and returns the new state.

NOTE: MONARC assumes that your state is immutable, i.e. every changes produces a new state. However MONARC is not tied to any implementation, so you can choose the framework you prefer, like immutable.js, immer, etc.

counter-reducer.js

export default function reduce(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return state
  }
}

Container

This is the component that we will render at the root of our application tree and that will manage the application’s state. It is the equivalent of Flux’s <Container /> or Redux’s <Provider />.

container.jsx

import counterReducer from './counter-reducer';
import { createContainer } from 'monarc';

function CounterContainer({ store }) {
  return (
    <div>
      <Header />
      <Counter value={store}/>
    </div>
  );
}

export default createContainer(CounterContainer, counterReducer);

After we’ve create our container, we just need to render it with the initial state of our app:

index.jsx

import CounterContainer from './containers/counter-container.jsx';
import ReactDOM from 'react-dom';
import React from 'react';

const node = document.getElementById('application');
const app  = (
  <CounterContainer initialState={0} />
);

ReactDOM.render(app, node);

Reducers

The createContainer function accepts a component and one or more reducers we created earlier. If your application gets big, you can split your reducers in different files and then pass them as an array to createContainer. When an action is fired, all the reducers will be called in order.

container.jsx

export default createContainer(CounterContainer, [ counterReducer, otherReducer ]);

If we want to enable undo / redo or auto-save, we just need to use the included plugins:

container.jsx

import { createContainer, withAutoSave, withUndoRedo } from 'monarc';
import counterReducer from './counter-reducer';

function CounterContainer() {
  ...
}

const reducer = withAutoSave(withUndoRedo(counterReducer), options);

export default createContainer(CounterContainer, reducer);

We can use both of them or just one of them as we wish. Each of these functions accept some options as the second parameter.

withUndoRedo

This plugin enables undo / redo management. We will be able to save our application’s state changes (up to a maximum value) and undo / redo to a previous state. There are also some behaviours which can be controlled by adding some flags to our actions.

Undo / redo options

This function accepts the following options:

function getState(state: any) => any
function setState(savedState: any, currentState: any) => any

Undo / redo flags

We can have a more fine-grained control over the undo / redo behaviour by adding some flags to any of our actions.

withAutoSave

This plugin enables the auto-save feature. Whenever the state changes, a timer will be started. When the timer expires, the current state of the application will be saved. We can also choose to save the state before the change.

The save will be immediately triggered on unmount and on the beforeunload event too.

NOTE: The function withAutoSave should always be called last.

Auto-save options

This function accepts the following options:

withDevTools

This plugin connects your application to the Redux DevTools extension. Follow the instructions described on Github to install the extension for your browser.

If you use immutable.js for your application state, remember to pass the correct options to the Redux DevTools, for example:

import { State } from './my-app-state-record';
import reducer from './my-app-reducer';
import Immutable from 'immutable';

let reducers = [ reducer ];

if (process.env.NODE_ENV !== 'production') {
  const options = {
    serialize: {
      immutable: Immutable,
      refs:      [ State ]
    }
  };

  reducers = withDevTools(reducers, options);
}

Options are described here.

Hooks

MONARC provides some hooks to access its state on every functional component of your application:

For class based components you can use the contextType attribute, or render the corresponding context <Consumer />.


Back to the index