"Loading..."

ArkhamJS vs Redux

Redux is very barebones as is ArkhamJS. Both are also based on the Flux architecture. The difference comes in to what functionality each framework handles on its own. While Redux gives you all the tools to manage a store, it really does nothing for you out of the box. Not only do you have to combine reducers and create stores, but you must also connect each component, redefining how you learned to develop React components.

ArkhamJS takes the basic fundamentals of Flux and adds the best of Redux (like the single store) and then polishes it off with making the development process easier to use without adding "magic". ArkhamJS takes care of the small tasks developers do not need to worry about and gives them flexibility on what really matters, the data.

While Redux forces the developer to conform to a certain way of app development, ArkhamJS gives the developer the freedom to develop to what the project requirements are rather than framework requirements.

Not only does redux require a developer to write a custom framework, for it to be a functional framework, it requires the use of middleware. If an asyncronous flow is required in a project (which most use), a middleware is needed. This can be redux-thunk, redux-saga, or quite a few others. The learning curve for redux remains high because while a developer may learn one middleware for one project, another may be using a different setup.

Quick List

Feature
ArkhamJS
Redux
Open source

Free to use within your project. Open to modify and/or customize source code to meet your needs.

Easy learning curve

Learning curve of the framework. The easier it is to pick up a new tool, the quicker it is to start development.

State management

Manage data in and out of the store.

Single store

All data is saved under a singular data strucure that contains an umbrella of data shared between components.

Store management

Creating, combining, and connecting stores.

Local state

Be able to use local component state.

React Native Support

Support react native along with AsyncStorage for caching.

Middleware

Extend the framework with custom or third-party middleware.

Officially Supported Features

The follow are officially supported by ArkhamJS. The importance here is compatibility and updates. Official modules/features are updated as the framework is updated, not having to rely on the timeline of a third-party module. Also third-party modules may become abandoned.

Immutable store

Its very dangerous to work with a store that is mutable. Data can be altered unwillingly. While ImmutableJS can be used in both frameworks, only ArkhamJS ensures all objects coming in and out of the framework are immutable.

[1]
Async actions

Dispatching multiple actions asynchronously.

[2]
Promises

There are times that actions should be dispatched after waiting on a response, like an API endpoint.

[3]
Caching

Support caching of data to retain data after an app has reloaded.

*
[4]
Logging

Send all data updates via dispatch to the console.

*
[5]
  • * - Requires official module.
  • 1 - Requires redux-immutable.
  • 2 - Requires redux-thunk or redux-saga.
  • 3 - Requires redux-promise.
  • 4 - Requires reselect or redux-cache.
  • 5 - Requires redux-logger.

Choosing a framework

Where is my data and how do I access it?

ArkhamJS

All data is saved in a single, global store. Data can be read from any component by importing and using the Flux object, Flux.getStore().

Redux

A Provider at the root is required to encapsulate the "Redux data bubble". In React this is usaully a <Provider/> tag at the root.

For a component to read data, it must be connected to the "Redux data bubble". Only connected components will be able to gain access to the store via the context which is handed down from the root provider. The store property uses a get method to access the data, store.getState().

How do I handle modification of data?

ArkhamJS

Data is written to the store via actions dispatched from the Flux object, Flux.dispatch.

Redux

A component must be connected to Redux. Only then will a component be able to access the store object to dispatch, store.dispatch.

How do I test my app?

ArkhamJS

All store management is handled and tested via ArkhamJS. As long as the stores are registered with ArkhamJS, there is no need for additional code.


/* Testing actions is pretty similar */
import * as AppActions from './AppActions';

describe('actions', () => {
  it('should create an action on update', async () => {
    const content = 'hello world';
    const testAction = await AppActions.update(content);
    const expectedAction = {type: 'APP_UPDATE', content};
    expect(testAction).toEqual(expectedAction)
  })
});

/* Testing components remains simple */
import * as React from 'react';

export class App extends Component { /* ... */ }

// Tests would include app the same as anywhere in the app,
// creating less confusion.
import {App} from './App';
                      

Redux

Since the store is manually created and combined, you must abstract this logic so it could be easily imported into unit tests.


/* Testing actions is pretty similar */
import * as AppActions from './AppActions';

describe('actions', () => {
  it('should create an action on update', () => {
    const content = 'hello world';
    const testAction = AppActions.update(content);
    const expectedAction = {type: 'APP_UPDATE', content};
    expect(testAction).toEqual(expectedAction)
  })
});

/* Testing components gets tricky. In the component itself,
   you would have 2 exports. */
import * as React from 'react';
import {connect} from 'react-redux';

// Use named export for unconnected component (for tests)
export class App extends React.Component { /* ... */ }

// Use default export for the connected component (for app)
export default connect(mapStateToProps)(App);

// Tests would include
import ConnectedApp, {App} from './App';
                      

Code Structure

How the codeis written is very important. Either you are familiar with a similar structure and the learning curve is low, or it will take some time to figure it out, raising the learning curve.

Let's use a simple example of updating content with a button click.

Initialization

The actions are similar but there is one difference. While ArkhamJS allows developers to handle the dispatches manually, Redux constricts that advantage by handling it internally. With ArkhamJS, you can trigger multiple dispatches within an action method. In Redux, you will need to use a middleware, like redux-thunk, to handle this type of behavior.

ArkhamJS


//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {Provider} from 'arkhamjs';
import {AppStore} from './appStore';

// Configure ArkhamJS and register stores
Flux.config({name: 'myApp'});
Flux.registerStores([AppStore]);

ReactDOM.render(<App />, document.getElementById('root'));

Initializing an ArkhamJS app, there is no need to wrap in any container. The Flux object is global and accessible in all child and sibling apps. The only

Redux


//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {Provider} from 'react-redux';
import {store} from './store';

// Wrap existing app in Provider
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

A <Provider /> is required at the root level of the app to create the "Redux data bubble" all connected components within this "bubble" will have access to the data. The problem with this approach is in the form of nested apps. Any nested <Provider /> components will not have access to the parent context, it will actually be overwritten in the child.

Actions

The actions are similar but there is one difference. While ArkhamJS allows developers to handle the dispatches manually, Redux constricts that advantage by handling it internally. With ArkhamJS, you can trigger multiple dispatches within an action method. In Redux, you will need to use a middleware, like redux-thunk, to handle this type of behavior.

 

ArkhamJS


// actions.js
import {Flux} from 'arkhamjs';

export const updateContent = (content) => {
  return Flux.dispatch({
    type: 'APP_UPDATE_CONTENT',
    content
  });
}

Actions are methods that dispatch an action object.

Redux


// actions.js
export const updateContent = (content) => {
  return {
    type: 'APP_UPDATE_CONTENT',
    content
  };
};

Each action method returns an action object.

Advantages

With ArkhamJS a developer has the felxibility to be able to dispatch an action whenever and whereever they choose. Lets take a look at a more complex example.

 

ArkhamJS


// actions.js
import {Flux} from 'arkhamjs';
import {Hunter} from 'rip-hunter';

export const updateContent = async (content) => {
  const canUpdate = Flux.getStore('app.canUpdate', false);

  if(canUpdate) {
    const quote = await Hunter.get('http://quotes.rest/qod.json?category=inspire');

    // If we also wanted to update a quote
    Flux.dispatch({
      type: 'QUOTES_UPDATE',
      quote: json
    });
  }

  Flux.dispatch({
    type: 'APP_UPDATE_CONTENT',
    content
  });
}

The action method can return anything or nothing. Just call a Flux.dispatch() when you need to dispatch an action. You can call as many dispatches as needed, sequencially or asyncronously. If you need the action object, just return the results from Flux.dispatch() to get a Promise.

Redux


// actions.js
import {Hunter} from 'rip-hunter';

export const updateContent = (content) => {
  return (dispatch, getState) => {
    const {canUpdate} = getState();

    if(canUpdate) {
      const quote = await Hunter.get('http://quotes.rest/qod.json?category=inspire');

      await dispatch({
        type: 'QUOTES_UPDATE',
        quote
      });
    }

    dispatch({
      type: 'APP_UPDATE_CONTENT',
      content
    });
  };
};

// store.js
import {createStore, applyMiddleware} from 'redux';
import {thunk} from 'redux-thunk';

export function configureStore(initialState = {}) {
  // Create store and define default values.
  // Apply middleware, if any.
  return createStore(reducers, initialState, applyMiddleware(thunk));
};

export const store = configureStore();

We would need to either return a delayed function, or use a middleware, like redux-thunk .

Stores

Once an action is dispatched, it is then captured and then updates the application data. In ArkhamJS, these are called stores. Redux calls them reducers.

 

ArkhamJS


// appStore.js
import {Store} from 'arkhamjs';

// Extend the ArkhamJS Store class.
export class AppStore extends Store {
  constructor() {
    // Set the store name.
    super('app');
  }

  initialState() {
    // Define default values.
    return {canUpdate: true, content: ''};
  }

  onAction(type, data, state) {
    // Define how each action type will affect the state.
    switch(type) {
      case 'APP_UPDATE_CONTENT':
        return {...state, content: data.content};
      default:
        return state;
    }
  }
}

The store is created within a single class. The name of the store is set in the constructor. This is how this particular branch of the data is referenced. Initial values are set. And finally, data is updated depending on the action type. Classes keep the logic of the stores centrally located.

ArkhamJS combines your stores for you.

Redux


// reducer.js
import {combineReducers} from 'redux';

export const content = (state = {}, action) => {
  // Define how each action type will affect the state.
  switch (action.type) {
    case 'APP_UPDATE_CONTENT':
      return action.content;
    default:
      return state;
  }
};

// Combine all the reducers into one.
export const reducers = combineReducers({content});

// store.js
import {createStore} from 'redux';

export function configureStore(initialState = {}) {
  // Create store and define default values.
  // Apply middleware, if any.
  return createStore(reducers, initialState);
};

export const store = configureStore();

Storing values happen between two parts. Although the parts can be within the same file, it is usually best pracrtice to keep them in separate files. The first part creates the reducer. The reducer updates the data depending on the action type. The second part creates the store with the initial values. Lots of moving parts.

Redux requires a manual concatenation of reducers.

Components

A big consideration is rendering performance. In React, unless you forceUpdate, a component will only re-render when the state changes. Three are 3 types of components: Component (re-renders on a change in state with deep comparison), PureComponent (re-renders on a change with a shallow comparison), JSX (no re-render unless a parent component re-renders). With Redux a component's state is handed off to the framework to manage. No state is used, instead Redux updates the props of the component and renders on a shallow compare.

ArkhamJS maintains the React logic for rendering, nothing has changed. A component can utilize any type of component (Component, PureComponent, etc.). Can update using props along with shouldComponentUpdate and/or state, where setState will traditionally update and render the component if necessary.

 

ArkhamJS


import {Flux} from 'arkhamjs';
import React from 'react';
import {updateContent} from './actions';

// App.js
export class App extends React.Component {
  constructor(props) {
    super(props);

    // Bind all methods
    this.onUpdate = this.onUpdate.bind(this);

    // Set initial state
    this.state = {
      content: Flux.getStore('app.content', '')
    };
  }

  componentWillMount() {
    // Manually listen for actions
    Flux.on('APP_CONTENT_UPDATE', this.onUpdate);
  }

  componentWillUnmount() {
    Flux.off('APP_CONTENT_UPDATE', this.onUpdate);
  }

  onUpdate() {
    const content = Flux.getStore('app.content', '');
    this.setState({content});
  }

  render() {
    return (
      <div>
        <h1>Hello World!</h1>

        <p>this.state.content</p>
        <button onClick={() => updateContent('This is a demo!')}>
          Update
        </button>
      </div>
    );
  }
}

The dispatch is manually called.

Redux


import React from 'react';
import {connect} from 'react-redux';
import {updateContent} from './actions';

// App.js
export class App extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello World!</h1>

        <p>this.props.content</p>
        <button onClick={() => this.props.updateContent({content: 'This is a demo!'})}>
          Update
        </button>
      </div>
    );
  }
}

// AppContainer.js
const mapStateToProps = (state, ownProps) => ({content: state.content});
const mapDispatchToProps = {updateContent};
const AppContainer = connect(mapStateToProps, mapDispatchToProps)(App);

export default AppContainer;

The dispatch is handled automatically.