0.4.0 • Published 7 years ago

flux-composer v0.4.0

Weekly downloads
4
License
ISC
Repository
github
Last release
7 years ago

Flux Composer

A library providing components for the Flux methodology that can be used in a compositional manner.

This library contains:

  • A dispatcher implementation
  • A notifier for your stores to notify views of changes
  • A mechanism for handling optimistic updates

Dispatcher

A page should only use a single instance of a dispatcher. For this reason, it is given as a singleton import.

import dispatcher from "<path>/dispatcher";

dispatcher.addStore(store)

Adds a store to the dispatcher. The store will receive any action dispatched. store is any object that has the name property as a string and the handleAction property as a function.

dispatcher.dispatch(action)

Dispatches an action to all stores synchronously. An action can be anything you like but is usually an object. The stores will receive actions through their handleAction methods.

dispatcher.waitFor(...stores)

Waits for the stores with the given names to be updated before returning. Give each store name as a separate parameter. This method must only be used within a store as it is being dispatched to.

Notifier

This module is used to create notifiers which register listeners and then call those listeners on demand. Stores can add listen() to their public API so that views can add listeners to them. Stores can then use notify() internally to call all of those listeners.

import createNotifier from "<path>/notifier";

const notifier = createNotifier();

notifier.listen(eventName, func)

Registers a listener to a named event. This method should be composed with your store's public API so that views can update themselves. Listeners are passed no data when notified, views should instead retrieve data from the store via other public API methods.

notifier.notify(eventName)

Calls all listeners subscribed to the given event name. If there are no listeners to the event then this function does nothing. This method should be used privately within your store.

notifier.unlisten(eventName, func)

Unregisters a listener from a named event. This method is the opposite of listen(). A listener that has been unregistered from a named event will no longer be called by notify() for that event name. If the listener is listening for any other events from this notifier, those will be unaffected. This method should be composed with your store's public API.

Flag Notifier

This module is used to create notifiers which register listeners against names and then calls those listeners according to a flag-raising model. A name can be flagged by this notifier and later listeners will be fired for all flagged names at once. Raising a flag multiple times will not cause a listener to be called multiple times, only once when the notifications are all dispatched together.

import createFlagNotifier from "<path>/flag-notifier";

const flagNotifier = createFlagNotifier();

flagNotifier.listen(flagName, func)

Registers a listener to a named flag. This method should be composed with your store's public API so that views can update themselves. Listeners are passed no data when notified, views should instead retrieve data from the store via other public API methods.

flagNotifier.flag(flagName)

Raises a named flag. Raising the same named flag more than once has no effect.

flagNotifier.notify()

Calls all the listeners of all of the raised flags. All raised flags are then lowered.

flagNotifier.unlisten(flagName, func)

Unregisters a listener from a named flag. This method is the opposite of listen(). A listener that has been unregistered from a flag name will no longer be called by notify() for that flag name. If the listener is listening for any other flags from this notifier, those will be unaffected. This method should be composed with your store's public API.

Examples

flagNotifier.listen("example", listener);
flagNotifier.notify(); // Listener isn't called, the "example" flag has not been raised.
flagNotifier.listen("example", listener);
flagNotifier.flag("example");
flagNotifier.notify(); // Listener is called once
flagNotifier.listen("example", listener);
flagNotifier.flag("example");
flagNotifier.flag("example");
flagNotifier.notify(); // Listener is still only called once
flagNotifier.listen("example", listener);
flagNotifier.flag("example");
flagNotifier.notify(); // Listener is called here.
flagNotifier.notify(); // The listener isn't called here because the previous notify lowered all flags.

Optimism

This module is used to handle optimistic updates to store state. This implementation uses a base state (the state of the store before any optimistic action is applied) and an array of actions (some of which are optimistic) that are applied in sequence to the base state to obtain the optimistic state.

import {optimisticReduction} from "<path>/optimism";

Actions that are optimistic must have a meta object that contains an optimistic boolean and an optimisticId. For example:

const action = {
    type: "SOME_TYPE",
    payload: "some payload",
    meta: {
        optimistic: true,
        optimisticId: 0
    }
}

Actions with optimistic set to true are optimistic; these are actions waiting for something to settle them. These will be queued rather than merged with the store's base state so that the store can be "rolled back" to before the optimistic action was applied.

Actions with optimistic set to false are the settling actions. These can be merged into the base state of a store.

Actions with an optimisticId will replace a queued action with an identical optimisticId. The id can be anything; matches are determined using the === comparator.

optimisticReduction(baseState, queuedActions, reducer, action)

This function should be called when a store handles an action.

Arguments:

  • baseState should be the state of the whole store.
  • queuedActions should be an empty array on the first call and the queuedActions array returned from the previous time this function was called every time afterwards.
  • reducer should be a reduction function that takes the store state as the first argument, the action being handled as the second, and returns the store state after the action has been applied.
  • action is the action being handled.

This function returns an object with three items: baseState, queuedActions, and optimisticState. optimisticState is the state of the store that should be exposed to consumers.

Example

This example shows an store that increments a single number that is also stored on a server

let queuedActions = [];
let baseState = 0;
let optimisticState;

function reducer(state, action) {
    if(action.type === "INCREMENT" && !action.error) {
        return state + 1; 
    }
}

// The store is incremented optimistically.
let action = {
    type: "INCREMENT",
    meta: {
        optimistic: true,
        optimisticId: 0
    }
};
({
    baseState,
    queuedActions,
    optimisticState
} = optimisticReduction(baseState, queuedActions, reducer, action));
// baseState = 0, optimisticState = 1

// The client receives a success message from the server indicating that the increment was successful.
action = {
    type: "INCREMENT",
    meta: {
        optimistic: false,
        optimisticId: 0
    }
};
({
    baseState,
    queuedActions,
    optimisticState
} = optimisticReduction(baseState, queuedActions, reducer, action));
// baseState = 1, optimisticState = 1

// The store is incremented optimistically again.
action = {
    type: "INCREMENT",
    meta: {
        optimistic: true,
        optimisticId: 1
    }
};
({
    baseState,
    queuedActions,
    optimisticState
} = optimisticReduction(baseState, queuedActions, reducer, action));
// baseState = 1, optimisticState = 2

// The client receives a failure message from the server indicating that the increment was unsuccessful.
action = {
    type: "INCREMENT",
    error: true,
    meta: {
        optimistic: false,
        optimisticId: 1
    }
};
({
    baseState,
    queuedActions,
    optimisticState
} = optimisticReduction(baseState, queuedActions, reducer, action));
// baseState = 1, optimisticState = 1
0.4.0

7 years ago

0.3.0

7 years ago

0.2.1

8 years ago

0.1.0

8 years ago