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

Migrating from Flux

One of the goals of MONARC is to ease the migration from Flux to a more modern and maintained solution to manage an application state. That means you should be able to migrate your existing applications with just a couple of changes.

These instructions will help you migrating from a Flux application using the ReduceStore / Container pattern with actions dispatched through Flux’s Dispatcher.

Dispatcher

First we need to replace Flux’s dispatcher with a new one. Usually the dispatcher is put in a separate module, which is then imported and used by the various action creators and to setup the store. All we need to do is to replace it with a fake one.

before

import { Dispatcher } from 'flux';
export default new Dispatcher();

after

export default { dispatch: null };

The dispatch attribute will later be replaced by the real dispatch function.

CAVEATS

Action creators

We should not need to make any changes to our existing action creators.

before

import dispatcher from './dispatcher';

export function increment(qty) {
  dispatcher.dispatch({ type: 'INCREMENT', qty });
}

after

import dispatcher from './dispatcher';

export function increment(qty) {
  dispatcher.dispatch({ type: 'INCREMENT', qty });
}

Store

The store is now set up in the container, since React’s hooks and context are tied to the components. This means that our existing stores can be deleted. This is assuming we have a store which simply subscribes to the dispatcher and that our reducers are neatly organized in one or more separate files. Here is an example of such a basic store:

import { ReduceStore } from 'flux/utils';
import reduce from './counter-reducer';
import dispatcher from './dispatcher';

class CounterStore extends ReduceStore {
  getInitialState() {
    return 0;
  }

  reduce(state, action) {
    return reduce(state, action);
  }
}

export default new CounterStore(dispatcher);

Reducers

We should not need to make any changes to our existing reducers.

before

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

after

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

Container

The store is now set up in the container. To mimic the Flux pattern, where the container updates its state when the store changes and then passes it down to its children, our new container will be rendered with a store prop which contains the store current state. Please note that in the createContainer function call we now have to pass the dispatcher as well.

before

import CounterStore from './counter-store';
import { Container } from 'flux/utils';
import { Component } from 'react';

class CounterContainer extends Component {
  static getStores() {
    return [ CounterStore ];
  }

  static calculateState(prevState) {
    return {
      counter: CounterStore.getState()
    };
  }

  render() {
    return (
      <CounterUI counter={this.state.counter} />
    );
  }
}

const container = Container.create(CounterContainer);

after

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

function CounterContainer({ store }) {
  return (
    <CounterUI counter={store} />
  );
}

CounterContainer.propTypes = {
  store: PropTypes.object.isRequired
};

const container = createContainer(CounterContainer, counterReducer, dispatcher);

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

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);

Back to the index