NAV Navbar
javascript typescript
  • Introduction
  • Getting Started
  • Stores
  • Actions
  • Reactions
  • Data Flow
  • Methods
  • Add Storage
  • Add Middleware
  • Add-ons
  • Introduction

    ArkhamJS is a lightweight framework that can accommodate a project of any size. From small start-up ideas to large enterprise projects. ReactJS is an amazing library but unfortunately, it is not a framework. Although the creators of ReactJS recommend using React in a Flux architecture, there is no official framework. The result is a wide variety of great third-party frameworks. Our goal is to create a simple framework with flexibility. And thus came ArkhamJS.

    Features

    Lightweight

    The framework is small. The bulk of your app should lay within your code, not the framework. While larger frameworks come with lots of "magic", they become very limited when new features arise within your project. ReactJS is very powerful in itself. ArkhamJS simply complements it.

    Typescript

    Compatible with typescript. Definitions are included to support your Typescript project.

    Single Store

    All data is stored within a single store. The data can be accessed through all your views and components. Data is organized into multiple stores within the single store.

    Immutability

    To maintain this singular source of truth, all data has a one way update path. Preventing leaks and data referencing snaking through the state tree. ArkhamJS ensures all objects in and out of the framework are immutable. When a state changes, the value will not be be referenced.

    Cache

    Your single store can be persisted in a cache. Caches available are browser (local or session), React Native's AsyncStorage, and NodeJS. While caching is optional, it can be very useful when saving state.

    Debugger

    The most important factor in choosing a framework is how easy it is to build with it. And with building comes debugging. A detailed debugging logger is available with the framework. When included, it will display any actions that come through the framework. Making the previous and new state visible to the developer. Great way to make your data transparent! Supported browsers: Chrome, Firefox, and Safari.

    Getting Started

    Source files

    Github

    The ArkhamJS project is regularly maintained on GitHub. All contributions, and suggested features are welcome.

    Download the Zip

    Although we recommend installing the node module, you may access the source files by downloading the latest .zip (~25kb).

    Installation

    ArkhamJS

    Install ArkhamJS

    // Install using npm:
    $ npm install --save @nlabs/arkhamjs
    
    // Install using npm:
    $ npm install --save @nlabs/arkhamjs
    

    ArkhamJS can be used in all modern browsers as well as within NodeJS and React Native apps.

    Importing

    // Import for ArkhamJS
    import {Flux} from '@nlabs/arkhamjs';
    
    // Import for ArkhamJS
    import {Flux} from '@nlabs/arkhamjs';
    

    Then with a module bundler like webpack that supports either CommonJS or ES2015 modules, use as you would anything else:

    Starter Skeletons

    For the quickest way to get started, we setup a skeleton to get started. Get the latest version and have a sample site up within minutes!

    The projects are complete with a pre-existing file structure, sample layout views, components, and even server setup with Webpack.

    Stores

    Example store as a function

    export const defaultState = {
      content: 'default'
    };
    
    export const app = (type, data, state = defaultState) => {
      // Define how each action type will affect the state.
      switch(type) {
        case 'APP_CONTENT_UPDATE':
          return {...state, content: data.content};
        case 'APP_CONTENT_RESET':
          return defaultState;
        default:
          return state;
      }
    }
    
    export const defaultState = {
      content: 'default'
    };
    
    export const app = (type: string, data: any, state: any = defaultState) => {
      // Define how each action type will affect the state.
      switch(type) {
        case 'APP_CONTENT_UPDATE':
          return {...state, content: data.content};
        case 'APP_CONTENT_RESET':
          return defaultState;
        default:
          return state;
      }
    }
    

    Example store as a class

    import {Store} from '@nlabs/arkhamjs';
    
    // Extend the ArkhamJS Store class.
    export class AppStore extends Store {
      constructor() {
        // Set the store name.
        super('app');
      }
    
      initialState() {
        // Define default values.
        return {
          content: 'default'
        };
      }
    
      onAction(type, data, state) {
        // Define how each action type will affect the state.
        switch(type) {
          case 'APP_CONTENT_UPDATE':
            return {...state, content: data.content};
          case 'APP_CONTENT_RESET':
            return this.initialState();
          default:
            return state;
        }
      }
    }
    
    import {Store} from '@nlabs/arkhamjs';
    
    // Extend the ArkhamJS Store class.
    export class AppStore extends Store {
      constructor() {
        // Set the store name.
        super('app');
      }
    
      initialState(): object {
        // Define default values.
        return {
          content: 'default'
        };
      }
    
      onAction(type: string, data: any, state: any): object {
        // Define how each action type will affect the state.
        switch(type) {
          case 'APP_CONTENT_UPDATE':
            return {...state, content: data.content};
          case 'APP_CONTENT_RESET':
            return this.initialState();
          default:
            return state;
        }
      }
    }
    

    ArkhamJS runs off of a single store object. That root store is then branched off to help organize your data. Each branch is a property in the root store object.

    ArkhamJS store

    Constructing a store

    All stores should extend the Store class. There are three methods to focus on: constructor, initialState, and onAction.

    constructor

    Let's start with giving your store a name. This name will be referenced when accessing you store's data. In the example, Flux.getState('app.test'), we would be traversing the app store and obtaining the test property. We define the name of the store in the constructor, setting the parameter of super to the name.

    initialState

    It is best practice to give all your properties a default value but not required. When ArkhamJS is initialized, it will grab the defaults to be used as initial values before any action is dispatched. Values can be any value, including numbers, strings, arrays, objects, and functions.

    onAction

    When an action is dispatched, it first calls triggers the stores before an event is dispatched to the view layer. As it hits each store, you can use the switch conditional to alter the data. Each case must return the full object of that partial store. So in the scenario of the example, we would return the app object, the app state.

    Actions

    import {Flux} from '@nlabs/arkhamjs';
    import {AppConstants} from '../constants/AppConstants';
    
    export const testAction = (str) => {
        ...
        // Dispatch our action.
        return Flux.dispatch({type: AppConstants.TEST, demo: str});
      }
    };
    
    import {Flux, FluxAction} from '@nlabs/arkhamjs';
    import {AppConstants} from '../constants/AppConstants';
    
    export const testAction = (str): FluxAction => {
        ...
        // Dispatch our action.
        return Flux.dispatch({type: AppConstants.TEST, demo: str});
      }
    };
    

    Actions are where all the excitement happens. They usually will contain your API calls, file reads/writes, and promises. When your data is pulled from an external source or generated within the app, you'll want to take your results and drop them into an action object and shoot them out with a dispatch call.

    Action objects

    An action object is simple an object with at least one required property, the type. The type is a unique string that identifies the action. It is used within the stores to indicate how the data should be added to the store. It also is used as the event type. All types are case sensitive. And although it is best practice to keep all type strings as constants, you can simply use a string if you wanted.

    In addition to the type property, you can add as many property key/values as you wish. All will be sent with the action to the store and to all listeners. In our example, we are adding a demo property to set the str string parameter.

    Dispatching

    Once we have an action object, we want to dispatch it to the framework. We do this by calling Flux.dispatch(action). ArkhamJS will check every store to see if the type matches any cases. If so, it will update the data in the stores. After all stores have been updated, an event will be dispatched to the Flux object, where any view/component listening in to that event will be triggered.

    Reactions

    The third and final piece are the reactions. Reactions are most commonly known as the view layer but can also be components with no view. Services, utilities, and functional components can be included in this category as well.

    This is where data updates are initiated. All updates are triggered by some time of user interaction, timer, or server response.

    React and React Native

    The view layer is created by React. React has a fast and concise rendering algorithm -- which was even further refined in React 16 with React Fiber. Making re-rendering isolated to only those components that need to be updated.

    ArkhamJS view connection

    Data Flow

    Various ways to access store data in a functional component.

    import {Flux} from '@nlabs/arkhamjs';
    import {useFlux, useState} from '@nlabs/arkhamjs-utils-react';
    import React from 'react';
    import {testAction} from '../actions/AppActions';
    import {AppConstants} from '../constants/AppConstants';
    
    export const onAppTest = (data) => {
      // Solution 1
      const myTest = Flux.getState('app.test', ' default text');
    
      // Solution 2
      const myTest = data.test; // data.type = AppConstants.TEST
    
      ...
      setState({hello: myTest});
    };
    
    export const AppView = (props) => {
      const [state, setState] = useState({
        hello: ''
      });
      const {hello} = state;
    
      useFlux([
        {handler: onAppTest, type: AppConstants.TEST}
        ...
      ]);
    
      // Solution 3
      // Where testAction returns the Flux.dispatch for a
      // AppConstants.TEST action.
      testAction().then((data) => {
        const myTest = data.test;
        setState({hello: myTest});
      });
      ...
    }
    
    import {Flux} from '@nlabs/arkhamjs';
    import {useFlux, useState} from '@nlabs/arkhamjs-utils-react';
    import React from 'react';
    import {testAction} from '../actions/AppActions';
    import {AppConstants} from '../constants/AppConstants';
    
    export const onAppTest = (setState) => (data): void => {
      // Solution 1
      const myTest: string = Flux.getState('app.test', ' default text');
    
      // Solution 2
      const myTest: string = data.test; // data.type = AppConstants.TEST
    
      ...
      setState({hello: myTest});
    };
    
    export const AppView = (props): JSX.Element => {
      const [state, setState] = useState({
        hello: ''
      });
      const {hello} = state;
    
      useFlux([
        {handler: onAppTest(setState), type: AppConstants.TEST}
        ...
      ]);
    
      // Solution 3
      // Where testAction returns the Flux.dispatch for a
      // AppConstants.TEST action.
      testAction().then((data) => {
        const myTest: string = data.test;
        setState({hello: myTest});
      });
      ...
    }
    

    Various ways to access store data in a class component.

    import {Flux} from '@nlabs/arkhamjs';
    import React from 'react';
    import {testAction} from '../actions/AppActions';
    import {AppConstants} from '../constants/AppConstants';
    
    export class AppView extends React.Component {
      constructor(props) {
        ...
    
        // Bind methods
        this.onAppTest = this.onAppTest.bind(this);
      }
    
      componentWillMount() {
        Flux.on(AppConstants.TEST, this.onAppTest);
        ...
      }
    
      componentWillUnmount() {
        Flux.off(AppConstants.TEST, this.onAppTest);
        ...
      }
    
      onAppTest(data) {
        // Solution 1
        const myTest = Flux.getState('app.test', ' default text');
    
        // Solution 2
        const myTest = data.test; // data.type = AppConstants.TEST
    
        ...
      }
    
      someMethod() {
        // Solution 3
        // Where testAction returns the Flux.dispatch for a
        // AppConstants.TEST action.
        testAction().then((data) => {
          const myTest = data.test;
        });
      }
    
      async anotherMethod() {
        // Solution 4
        // Using async/await you can obtain an action synchronously
        const data = await testAction();
        const myTest = data.test;
      }
      ...
    
    import {Flux, FluxAction, FluxOptions} from '@nlabs/arkhamjs';
    import React from 'react';
    import {testAction} from '../actions/AppActions';
    import {AppConstants} from '../constants/AppConstants';
    
    export class AppView extends React.Component {
      constructor(props) {
        ...
    
        // Bind methods
        this.onAppTest = this.onAppTest.bind(this);
      }
    
      componentWillMount(): void {
        Flux.on(AppConstants.TEST, this.onAppTest);
        ...
      }
    
      componentWillUnmount() {
        Flux.off(AppConstants.TEST, this.onAppTest);
        ...
      }
    
      onAppTest(data: FluxAction): void {
        // Solution 1
        const myTest: string = Flux.getState('app.test', ' default text');
    
        // Solution 2
        const myTest: string = data.test; // data.type = AppConstants.TEST
    
        ...
      }
    
      someMethod(): void {
        // Solution 3
        // Where testAction returns the Flux.dispatch for a
        // AppConstants.TEST action.
        testAction().then((data) => {
          const myTest: string = data.test;
        });
      }
    
      async anotherMethod(): Promise<void> {
        // Solution 4
        // Using async/await you can obtain an action synchronously
        const data: FluxAction = await testAction();
        const myTest: string = data.test;
      }
      ...
    }
    

    The most important part of an app is the dynamic data.

    ArkhamJS data flow

    Accessing Data

    ArkhamJS is made to be flexible for your project. Thus, we have made it easy for you to access the most important part of your app, the data.

    There are three ways to access the data after an action has been dispatched.

    1. It is best practice to get a value from the Flux.getState() method. This will let you traverse down the store to the nested property value. If a value does not exist, a default value can be provided. You can call Flux.getState() anywhere in your view and/or action.
    2. Through the data parameter in your event listener callback.
    3. Sometimes you may not want an event listener for a dispatched action. In this case, you can chain a then method off your dispatch. Yes, each dispatch method returns a promise.

    Methods

    Configuration

    #config(options)

    Set configuration within root view component

    import {BrowserStorage} from '@nlabs/arkhamjs-storage-browser';
    import {Flux} from '@nlabs/arkhamjs';
    
    const env = 'development';
    const storage = new BrowserStorage({type: 'session'});
    
    Flux.config({
      name: 'MyApp',
      storage
    });
    
    import {BrowserStorage} from '@nlabs/arkhamjs-storage-browser';
    import {Flux} from '@nlabs/arkhamjs';
    
    const env: string = 'development';
    const storage: BrowserStorage = new BrowserStorage({type: 'session'});
    
    Flux.config({
      name: 'MyApp',
      storage
    });
    

    Set configuration options.

    Arguments

    Returns

    A promise with a null object.

    Events

    #on(eventType, data)

    Triggering and listening to events.

    import {Flux} from '@nlabs/arkhamjs';
    import React from 'react';
    import {AppConstants} from '../constants/AppConstants';
    
    export class AppView extends React.Component {
      constructor(props) {
        super(props);
    
        // Bind methods.
        this.onGetItems = this.onGetItems.bind(this);
    
        // Set initial state.
        this.state = {
          item: {}
        };
      }
    
      componentWillMount() {
        // Add a listener for GET_ITEM. When the event is
        // dispatched, the onGetItems method will be
        // called.
        Flux.on(AppConstants.GET_ITEM, this.onGetItems);
      }
    
      componentWillMount() {
        // Stop listening when the view is unmounted.
        Flux.off(AppConstants.GET_ITEM, this.onGetItems);
      }
    
      onGetItems() {
        // Get the item from the updated store.
        const item = Flux.getState('app.item', {});
        this.setState({item});
      }
      // Get item action would be triggered below with
      // a UI interaction.
      ...
    
    import {Flux} from '@nlabs/arkhamjs';
    import React from 'react';
    import {AppConstants} from '../constants/AppConstants';
    import {AppItemType} from '../types/AppTypes';
    
    export class AppView extends React.Component {
      constructor(props) {
        super(props);
    
        // Bind methods.
        this.onGetItems = this.onGetItems.bind(this);
    
        // Set initial state.
        this.state = {
          item: {}
        };
      }
    
      componentWillMount(): void {
        // Add a listener for GET_ITEM. When the event is
        // dispatched, the onGetItems method will be
        // called.
        Flux.on(AppConstants.GET_ITEM, this.onGetItems);
      }
    
      componentWillMount(): void {
        // Stop listening when the view is unmounted.
        Flux.off(AppConstants.GET_ITEM, this.onGetItems);
      }
    
      onGetItems(): void {
        // Get the item from the store.
        const item: AppItemType = Flux.getState('app.item', {});
        this.setState({item});
      }
      // Get item action would be triggered below with
      // a UI interaction.
      ...
    }
    

    Adds an event listener. It is called any time an action is dispatched to Flux, and some part of the state tree may potentially have changed. You may then call getState() to read the current state tree inside the callback.

    Arguments

    #off(eventType, data)

    Removes an event listener.

    Arguments

    #dispatch(action, silent)

    Dispatches an Action to all stores.

    Arguments

    Returns

    A promise with an action object.

    Stores

    #getState(name, default)

    Get store data

    // Get the root store object.
    Flux.getState();
    
    // Using a dot notation.
    Flux.getState('app.test', 'default');
    
    // Using an array to traverse.
    Flux.getState(['app', 'test'], 'default');
    
    // Get the root store object.
    Flux.getState();
    
    // Using a dot notation.
    Flux.getState('app.test', 'default');
    
    // Using an array to traverse.
    Flux.getState(['app', 'test'], 'default');
    

    Get the state tree. If only a particular store is needed, it can be specified.

    Arguments

    Returns

    The app store object.

    #setState(name, value)

    Set store data

    // Using a dot notation.
    Flux.setState('app.test', 'Hello World');
    
    // Using an array to traverse.
    Flux.setState(['app', 'test'], 'Hello World');
    
    // Using a dot notation.
    Flux.setState('app.test', 'Hello World');
    
    // Using an array to traverse.
    Flux.setState(['app', 'test'], 'Hello World');
    

    Used for unit testing. Set a store value. If only a particular store or property needs to be set, it can be specified. It is best practice to set update the state via actions, not directly using setState.

    Arguments

    Returns

    The updated store and returns the stored object.

    #getClass(name)

    Get a store class

    Flux.getClass('app');
    // In this example, will return the AppStore class.
    
    Flux.getClass('app');
    // In this example, will return the AppStore class.
    

    Get the store class object.

    Arguments

    Returns

    A store class object.

    #registerStores(stores)

    Register a store

    import {AppStore} from '../stores/AppStore';
    
    // Register stores to be used with the app.
    Flux.registerStores([AppStore]);
    
    import {AppStore} from '../stores/AppStore';
    
    // Register stores to be used with the app.
    Flux.registerStores([AppStore]);
    

    Registers stores with Flux. Use an array of classes to register multiple.

    Arguments

    Returns

    An array of store class objects.

    #deregisterStores(names)

    Remove a store from the app

    // Deregister stores from the app.
    Flux.deregisterStores(['app']);
    
    // Deregister stores from the app.
    Flux.deregisterStores(['app']);
    

    Deregisters stores from Flux. Use an array of names to deregister multiple stores.

    Arguments

    #clearAppData()

    Clear all app data stored in state.

    Flux.clearAppData();
    
    Flux.clearAppData();
    

    Removes all app data from state.

    Returns

    A boolean indicating if app data was successfully removed.

    State

    #getInitialState()

    Returns initial state

    Flux.getInitialState();
    
    Flux.getInitialState();
    

    Used for unit testing. Gets the initial state of the store.

    Returns

    The initial state of the store as an object.

    Middleware

    #addMiddleware(middleware)

    Add middleware

    const loggerMiddleware: Logger = new Logger();
    Flux.addMiddleware([loggerMiddleware]);
    
    const loggerMiddleware: Logger = new Logger();
    Flux.addMiddleware([loggerMiddleware]);
    

    Adds middleware from the framework. Middleware modules will be processed in the order they are added.

    Arguments

    #clearMiddleware()

    Remove all middleware

    Flux.clearMiddleware();
    
    Flux.clearMiddleware();
    

    Removes all middleware from the framework.

    Returns

    A boolean. True if the middleware has been successfully removed.

    #removeMiddleware(middleware)

    Remove middleware

    Flux.removeMiddleware(['logger']);
    
    Flux.removeMiddleware(['logger']);
    

    Removes middleware from the framework.

    Arguments

    Add Storage

    By default ArkhamJS does not use a persistent storage. All state data is stored in memory. ArkhamJS can persist data depending on the environment.

    Persist storage is the continuance of data after its cause is removed. This means that the data survives after the process with which it was created has ended. For a data store to be considered persistent, it must write to non-volatile storage so when the app reloads, the previously saved data is still available.

    ArkhamJS cache

    There are three official persistent stores available:

    Create custom

    Custom storage structure

    export class CustomStorage {
      constructor() {
        this.getStorageData = this.getStorageData.bind(this);
        this.setStorageData = this.setStorageData.bind(this);
      }
    
      getStorageData(key) {
        // ...
      }
    
      setStorageData(key, value) {
        // ...
      }
    }
    
    export class CustomStorage {
      constructor() {
        this.getStorageData = this.getStorageData.bind(this);
        this.setStorageData = this.setStorageData.bind(this);
      }
    
      getStorageData(key: string) {
        // ...
      }
    
      setStorageData(key: string, value: any) {
        // ...
      }
    }
    

    With so many ways of storing data, a custom storage plugin can be created to fit your needs. A storage class simply requires two methods:

    As long as these two methods are included, you can add as many helper functions as needed. For an in-depth example with options and static helper methods, you may take a look at the arkhamjs-storage-browser source code.

    #getStorageData(key)

    Get app data

    const storage = new CustomStorage();
    storage.getStorageData('dataKey');
    
    const storage = new CustomStorage();
    storage.getStorageData('dataKey');
    

    Get app data from storage. This method is used internally to access cached data.

    Arguments

    Returns

    A value from app storage.

    #setStorageData(key)

    Set app data

    const storage = new CustomStorage();
    storage.setSessionData('dataKey', 'dataValue');
    
    const storage = new CustomStorage();
    storage.setSessionData('dataKey', 'dataValue');
    

    Save app data to storage. This method is used internally to save data to cache.

    Arguments

    Returns

    A boolean indicating if data was successfully saved to storage.

    arkhamjs-storage-browser

    Configure and initialize

    import {BrowserStorage} from '@nlabs/arkhamjs-storage-browser';
    
    // Browser storage configuration
    const storage = new BrowserStorage({type: 'session'});
    
    // ArkhamJS configuration
    Flux.config({
      storage
    });
    
    import {BrowserStorage} from '@nlabs/arkhamjs-storage-browser';
    
    // Browser storage configuration
    const storage: BrowserStorage = new BrowserStorage({type: 'session'});
    
    // ArkhamJS configuration
    Flux.config({
      storage
    });
    

    HTML5 web storage provides two objects for storing data on the client: window.localStorage and window.sessionStorage. With web storage, applications can store data locally within the browser.

    Options

    Installing

    Install browser storage

    // Install using npm:
    $ npm install --save @nlabs/arkhamjs-storage-browser
    
    // Install using npm:
    $ npm install --save @nlabs/arkhamjs-storage-browser
    

    The browser storage plugin can be found as a node module at arkhamjs-storage-browser.

    .delLocalData(key)

    Remove data in local storage

    BrowserStorage.delLocalData('dataKey');
    
    BrowserStorage.delLocalData('dataKey');
    

    Remove an object from localStorage.

    Arguments

    Returns

    A boolean indicating if data was successfully removed from LocalStorage.

    .delSessionData(key)

    Set data in session storage

    BrowserStorage.delSessionData('dataKey');
    
    BrowserStorage.delSessionData('dataKey');
    

    Remove an object from sessionStorage.

    Arguments

    Returns

    A boolean indicating if data was successfully removed from sessionStorage.

    .getLocalData(key)

    Get data from local storage

    BrowserStorage.getLocalData('dataKey');
    
    BrowserStorage.getLocalData('dataKey');
    

    Get an object from localStorage.

    Arguments

    Returns

    A value from local storage.

    .getSessionData(key)

    Get data from session storage

    BrowserStorage.getSessionData('dataKey');
    
    BrowserStorage.getSessionData('dataKey');
    

    Get an object from sessionStorage.

    Arguments

    Returns

    A value from session storage.

    .setLocalData(key, value)

    Set data in local storage

    BrowserStorage.setLocalData('dataKey', 'Hello World');
    
    BrowserStorage.setLocalData('dataKey', 'Hello World');
    

    Save an object to localStorage.

    Arguments

    Returns

    A boolean indicating if data was successfully saved in localStorage.

    .setSessionData(key, value)

    Set data in session storage

    BrowserStorage.setSessionData('dataKey', 'Hello World');
    
    BrowserStorage.setSessionData('dataKey', 'Hello World');
    

    Save an object to sessionStorage.

    Arguments

    Returns

    A boolean indicating if data was successfully saved to sessionStorage.

    arkhamjs-storage-native

    Configure and initialize

    import {NativeStorage} from '@nlabs/arkhamjs-storage-native';
    
    // React Native storage configuration
    const storage = new NativeStorage();
    
    // ArkhamJS configuration
    Flux.config({
      storage
    });
    
    import {NativeStorage} from '@nlabs/arkhamjs-storage-native';
    
    // Storage configuration
    const storage: NativeStorage = new NativeStorage();
    
    // ArkhamJS configuration
    Flux.config({
      storage
    });
    

    React native uses a native storage used between both, Android and iOS, platforms, called AsyncStorage. AsyncStorage is a simple, unencrypted, asynchronous, persistent, key-value storage system that is global to the app.

    On iOS, AsyncStorage is backed by native code that stores small values in a serialized dictionary and larger values in separate files. On Android, AsyncStorage will use either RocksDB or SQLite based on what is available.

    More details on AsyncStorage can be found on the React Native web site.

    Installing

    Install React Native storage

    // Install using npm:
    $ npm install --save @nlabs/arkhamjs-storage-native
    
    // Install using npm:
    $ npm install --save @nlabs/arkhamjs-storage-native
    

    The React Native storage plugin can be found as a node module at arkhamjs-storage-native.

    .delAsyncData(key)

    Remove data in AsyncStorage

    NativeStorage.delAsyncData('dataKey');
    
    NativeStorage.delAsyncData('dataKey');
    

    Remove an object from AsyncStorage.

    Arguments

    Returns

    A boolean indicating if data was successfully removed from AsyncStorage.

    .getAsyncData(key)

    Get data from AsyncStorage

    NativeStorage.getAsyncData('dataKey');
    
    NativeStorage.getAsyncData('dataKey');
    

    Get an object from AsyncStorage.

    Arguments

    Returns

    A value from AsyncStorage.

    .setAsyncData(key, value)

    Set data in AsyncStorage

    NativeStorage.setAsyncData('dataKey', 'Hello World');
    
    NativeStorage.setAsyncData('dataKey', 'Hello World');
    

    Save an object to AsyncStorage.

    Arguments

    Returns

    A boolean indicating if data was successfully saved in AsyncStorage.

    arkhamjs-storage-node

    Install and initialize

    import {NodeStorage} from '@nlabs/arkhamjs-storage-node';
    
    // Node storage configuration
    const storage = new NodeStorage({
        continuous: true,
        dir: '/tmp',
        encoding: 'utf8',
        expiredInterval: 3 * 60 * 1000,
        forgiveParseErrors: false,
        interval: false,
        logging: false,
        parse: JSON.parse,
        stringify: JSON.stringify,
        ttl: false
      });
    
    // ArkhamJS configuration
    Flux.config({
      storage
    });
    
    import {NodeStorage} from '@nlabs/arkhamjs-storage-node';
    
    // Node storage configuration
    const storage: NodeStorage = new NodeStorage({
        continuous: true,
        dir: '/tmp',
        encoding: 'utf8',
        expiredInterval: 3 * 60 * 1000,
        forgiveParseErrors: false,
        interval: false,
        logging: false,
        parse: JSON.parse,
        stringify: JSON.stringify,
        ttl: false
      });
    
    // ArkhamJS configuration
    Flux.config({
      storage
    });
    

    Data is stored as JSON documents in the file system for persistence. App data is just in-memory, which will result in lightning fast retrievals and stores.

    Options

    Installing

    Install NodeJS persistent storage

    // Install using npm:
    $ npm install --save @nlabs/arkhamjs-storage-node
    
    // Install using npm:
    $ npm install --save @nlabs/arkhamjs-storage-node
    

    The NodeJS persistent storage plugin can be found as a node module at arkhamjs-storage-node.

    .clearPersistData()

    Clear all data

    NodeStorage.clearPersistData();
    
    NodeStorage.clearPersistData();
    

    Removes all keys from persistant data.

    Returns

    A promise resolving a boolean indicating if data was successfully removed.

    .delPersistData(key)

    Remove data

    NodeStorage.delPersistData('dataKey');
    
    NodeStorage.delPersistData('dataKey');
    

    Removes a key from persistant data.

    Arguments

    Returns

    A promise resolving a boolean indicating if data was successfully removed.

    .getPersistData(key)

    Get data

    NodeStorage.getPersistData('dataKey');
    
    NodeStorage.getPersistData('dataKey');
    

    Get a key value from persistant data.

    Arguments

    Returns

    A promise resolving a value from storage.

    .setAsyncData(key, value)

    Set data

    NodeStorage.setPersistData('dataKey', 'Hello World');
    
    NodeStorage.setPersistData('dataKey', 'Hello World');
    

    Saves data to persistent data.

    Arguments

    Returns

    A promise resolving a boolean indicating if data was successfully saved in storage.

    Add Middleware

    Middleware is a class that contains plugins to the framework for customization. You can add in existing middleware modules or create a custom class to include.

    Custom middleware

    Custom class

    export class CustomMiddleware {
      constructor() {
        this.name = 'customName';
    
        // Methods
        this.postDispatch = this.postDispatch.bind(this);
        this.preDispatch = this.preDispatch.bind(this);
      }
    
      preDispatch(action, previousStore) {
        // ... Alter action if needed
        return Promise.resolve(action);
      }
    
      postDispatch(action, nextStore) {
        // ... Alter action if needed
        return Promise.resolve(action);
      }
    }
    
    import {FluxAction} from '@nlabs/arkhamjs';
    
    export class CustomMiddleware {
      name: string = 'customName';
    
      constructor() {
        // Methods
        this.postDispatch = this.postDispatch.bind(this);
        this.preDispatch = this.preDispatch.bind(this);
      }
    
      preDispatch(action: FluxAction, previousStore): Promise<FluxAction> {
        // ... Alter action if needed
        return Promise.resolve(action);
      }
    
      postDispatch(action: FluxAction, nextStore: object): Promise<FluxAction> {
        // ... Alter action if needed
        return Promise.resolve(action);
      }
    }
    

    There are two requirements for a custom middleware module, it must have a name and there must be at least one plugin. You can provide multiple plugins within a middleware module. There are currently two types of plugins available:

    ArkhamJS middleware

    Logger

    Add the logger middleware

    import {Flux} from '@nlabs/arkhamjs';
    
    // Configure
    const logger = new Logger({
      debugLevel: LoggerDebugLevel.DISPATCH
    });
    
    // Add to ArkhamJS
    Flux.addMiddleware([logger]);
    
    import {Flux} from '@nlabs/arkhamjs';
    
    // Configure
    const logger: Logger = new Logger({
      debugLevel: LoggerDebugLevel.DISPATCH
    });
    
    // Add to ArkhamJS
    Flux.addMiddleware([logger]);
    

    Logs actions to the console log after each dispatch. Tracking changes in the state. View state actions and changes in detail.

    The logger also provides a way to wrap your console methods (log, warning, and error) to better facilitate enabling and disabling logging in console. Instead of forgotten logs or unnecessary error logging. You may also wrap your logs with a custom wrapper to send to analytics, add color, etc.

    Loggert screenshot

    Options

    Installation

    Install Logger

    // Install using npm:
    $ npm install --save @nlabs/arkhamjs-middleware-logger
    
    // Install using npm:
    $ npm install --save @nlabs/arkhamjs-middleware-logger
    

    The logger middleware can be found as a node module at arkhamjs-middleware-logger.

    #enableDebugger(toggle)

    Toggle debugger

    const logger = new Logger();
    logger.enableDebugger(LoggerDebugLevel.DISPATCH);
    
    const logger: Logger = new Logger();
    logger.enableDebugger(LoggerDebugLevel.DISPATCH);
    

    Turn on the console debugger to display each action call and store changes. By default the framework has the debugger disabled.

    Arguments

    #debugLog(obj1 [, obj2, ..., objN])

    Safely add debug logging

    const logger = new Logger();
    logger.debugLog('console log');
    
    const logger: Logger = new Logger();
    logger.debugLog('console log');
    

    Logs data in the console. Only logs when in debug mode. Will also call the debugLogFnc method set in the config.

    Arguments

    #debugInfo(obj1 [, obj2, ..., objN])

    Safely add info logging

    const logger = new Logger();
    logger.debugInfo('console info');
    
    const logger: Logger = new Logger();
    logger.debugInfo('console info');
    

    Logs informational messages to the console. Will also call the debugInfoFnc method set in the config.

    Arguments

    #debugError(obj1 [, obj2, ..., objN])

    Safely add error logging

    const logger = new Logger();
    logger.debugError('console error');
    
    const logger: Logger = new Logger();
    logger.debugError('console error');
    

    Logs errors in the console. Will also call the debugErrorFnc method set in the config.

    Arguments

    Add-ons

    Components used alongside the framework. Containers, UI components, services, etc.

    arkhamjs-utils-react

    Utilities and helpers to assist with React components.

    Installation

    Install Logger

    // Install using npm:
    $ npm install --save @nlabs/arkhamjs-utils-react
    
    // Install using npm:
    $ npm install --save @nlabs/arkhamjs-utils-react
    

    The react utils can be found as a node module at arkhamjs-utils-react.

    useFlux

    useFlux hook

    import {useFlux} from '@nlabs/arkhamjs-utils-react';
    import React from 'react';
    
    export const onSuccess = (action) => {
      ...
    };
    
    export const MyComponent = () => {
      useFlux([
        {handler: onSuccess, type: 'SUBMIT_SUCCESS'}
      ]);
    
      return <div></div>;
    }
    
    import {useFlux} from '@nlabs/arkhamjs-utils-react';
    import React from 'react';
    
    export const onSuccess = (action) => {
      ...
    };
    
    export const MyComponent = () => {
      useFlux([
        {handler: onSuccess, type: 'SUBMIT_SUCCESS'}
      ]);
    
      return <div></div>;
    }
    

    Adds an event listener when component mounts and removes on unmount.

    useState

    useState hook

    import {Flux} from '@nlabs/arkhamjs';
    import {useFlux, useState} from '@nlabs/arkhamjs-utils-react';
    import React, {useEffect} from 'react';
    
    export const onSuccess = (setState) => ({content}) => {
      ...
      setState({content});
    };
    
    export const MyComponent = () => {
      useFlux([
        {handler: onSuccess, type: 'SUBMIT_SUCCESS'}
      ]);
    
      const [state, setState] = useState({
        content: '',
        title: 'Demo'
      });
      const {content, title} = state;
    
      useEffect(() => {
        // On mount dispatch a sample trigger
        Flux.dispatch({content: 'success', type: 'SUBMIT_SUCCESS'});
      }, []);
    
      return (
        <div>
          <h1>{title}</h1>
          <p>{content}</p>
        </div>
      );
    }
    
    import {Flux} from '@nlabs/arkhamjs';
    import {useFlux, useState} from '@nlabs/arkhamjs-utils-react';
    import React, {useEffect} from 'react';
    
    export const onSuccess = (setState) => ({content}) => {
      ...
      setState({content});
    };
    
    export const MyComponent = () => {
      useFlux([
        {handler: onSuccess, type: 'SUBMIT_SUCCESS'}
      ]);
    
      const [state, setState] = useState({
        content: '',
        title: 'Demo'
      });
      const {content, title} = state;
    
      useEffect(() => {
        // On mount dispatch a sample trigger
        Flux.dispatch({content: 'success', type: 'SUBMIT_SUCCESS'});
      }, []);
    
      return (
        <div>
          <h1>{title}</h1>
          <p>{content}</p>
        </div>
      );
    }
    

    Similar to the useState function in React except works like the state in a component, merging new object values with the previous values instead of overwriting the whole object. Strings and numbers can also be used instead of objects but will be overwritten.

    arkhamjs-views-react

    React view containers to simplify routing. Uses React Router to route the views. Updates title in the browser on successful route. ArkhamRouteActions wraps actions around the history. Easily notify your components on a route action.

    Installation

    Install views

    // Install using npm:
    $ npm install --save @nlabs/arkhamjs-views-react
    
    // Install using npm:
    $ npm install --save @nlabs/arkhamjs-views-react
    

    The react views can be found as a node module at arkhamjs-views-react.

    ArkhamRouteActions

    Import ArkhamRouteActions

    import {ArkhamRouteActions} from '@nlabs/arkhamjs-views-react';
    
    import {ArkhamRouteActions} from '@nlabs/arkhamjs-views-react';
    

    Import ArkhamRouteActions to gain access to the route history methods.

    ArkhamRouteActions.goBack()

    Go back

    ArkhamRouteActions.goBack();
    
    ArkhamRouteActions.goBack();
    

    Go to the previous view.

    Returns

    A promise resolving an action. Actions props include:

    ArkhamRouteActions.goForward()

    Go forward

    ArkhamRouteActions.goForward();
    
    ArkhamRouteActions.goForward();
    

    Go to the next view.

    Returns

    A promise resolving an action. Actions props include:

    ArkhamRouteActions.goReplace(path [, params])

    Replace view

    ArkhamRouteActions.goReplace('/login', {token});
    
    ArkhamRouteActions.goReplace('/login', {token});
    

    Replace the current view with a new view. This method will also replace the view in the history.

    Arguments

    Returns

    A promise resolving an action. Actions props include:

    ArkhamRouteActions.goto(path [, params])

    Go to a new view

    ArkhamRouteActions.goto('/details', {user});
    
    ArkhamRouteActions.goto('/details', {user});
    

    Push another view into the history.

    Arguments

    Returns

    A promise resolving an action. Actions props include:

    ArkhamRouteActions.updateTitle(title)

    Update title

    ArkhamRouteActions.updateTitle('New page title');
    
    ArkhamRouteActions.updateTitle('New page title');
    

    Manually update page title in the browser.

    Arguments

    Returns

    A promise resolving an action. Actions props include:

    Root view component example.

    import {Logger, LoggerDebugLevel} from '@nlabs/arkhamjs-middleware-logger';
    import {Arkham} from '@nlabs/arkhamjs-views-react';
    import {Flux} from '@nlabs/arkhamjs';
    import * as React from 'react';
    import {AppActions} from '../actions/AppActions';
    import {AppConstants} from '../constants/AppConstants';
    import {AppStore} from '../stores/AppStore';
    
    export class AppView extends React.Component {
      fluxOptions;
      stores;
    
      constructor(props) {
        super(props);
    
        // Set the initial state.
        this.state = {
          myTest: ''
        };
    
        // Initialize Flux with custom configuration (optional)
        this.fluxOptions = {
          // Name of your app.
          name: 'MyApp'
        };
    
        // Register stores
        this.stores = [AppStore];
    
        // Middleware
        const logger: Logger = new Logger({
          debugLevel: LoggerDebugLevel.DISPATCH
        });
        this.middleware = [logger];
    
        // Bind methods
        this.onAppTest = this.onAppTest.bind(this);
      }
    
      componentWillMount() {
        // Add event listener for our action. Here we are
        // listening for the TEST action to be dispatched.
        // When triggered, will call method, onAppTest.
        Flux.on(AppConstants.TEST, this.onAppTest);
    
        // When the component loads and is mounted, it
        // will call the AppActions.test method. This
        // dispatches our TEST action with the string
        // provided ('Hello World').
        AppActions.test('Hello World');
      }
    
      componentWillUnmount() {
        // Stop listening for the action when we unmount.
        Flux.off(AppConstants.TEST, this.onAppTest);
      }
    
      onAppTest() {
        // Get the string from the store. If the string
        // happened to be null, we provided a default.
        const myTest = Flux.getState('app.test', ' default text');
    
        // Show the output in the console.
        console.log('onAppTest::myTest', myTest);
    
        // Set state to re-render component.
        this.setState({myTest});
      }
    
      render() {
        return (
          <Arkham config={this.fluxOptions} stores={this.stores} middleware={this.middleware}>
            {this.state.myTest}
          </Arkham>
        );
      }
    }
    
    import {Logger, LoggerDebugLevel} from '@nlabs/arkhamjs-middleware-logger';
    import {Arkham} from '@nlabs/arkhamjs-views-react';
    import {Flux, FluxOptions, Store} from '@nlabs/arkhamjs';
    import * as React from 'react';
    import {AppActions} from '../actions/AppActions';
    import {AppConstants} from '../constants/AppConstants';
    import {AppStore} from '../stores/AppStore';
    
    export class AppView extends React.Component {
      private fluxOptions: FluxOptions;
      private stores: Store[];
    
      constructor(props) {
        super(props);
    
        // Set the initial state.
        this.state = {
          myTest: ''
        };
    
        // Initialize Flux with custom configuration (optional)
        this.fluxOptions = {
          // Name of your app.
          name: 'MyApp'
        };
    
        // Register stores
        this.stores = [AppStore];
    
        // Middleware
        const logger: Logger = new Logger({
          debugLevel: LoggerDebugLevel.DISPATCH
        });
        this.middleware = [logger];
    
        // Bind methods
        this.onAppTest = this.onAppTest.bind(this);
      }
    
      componentWillMount(): void {
        // Add event listener for our action. Here we are
        // listening for the TEST action to be dispatched.
        // When triggered, will call method, onAppTest.
        Flux.on(AppConstants.TEST, this.onAppTest);
    
        // When the component loads and is mounted, it
        // will call the AppActions.test method. This
        // dispatches our TEST action with the string
        // provided ('Hello World').
        AppActions.test('Hello World');
      }
    
      componentWillUnmount(): void {
        // Stop listening for the action when we unmount.
        Flux.off(AppConstants.TEST, this.onAppTest);
      }
    
      onAppTest(): void {
        // Get the string from the store. If the string
        // happened to be null, we provided a default.
        const myTest = Flux.getState('app.test', ' default text');
    
        // Show the output in the console.
        console.log('onAppTest::myTest', myTest);
    
        // Set state to re-render component.
        this.setState({myTest});
      }
    
      render(): JSX.Element {
        return (
          <Arkham config={this.fluxOptions} stores={this.stores}>
            {this.state.myTest}
          </Arkham>
        );
      }
    }