Skip to content

Instantly share code, notes, and snippets.

@kyldvs
Created August 14, 2015 16:04
Show Gist options
  • Save kyldvs/8a7031514623c17d3f4b to your computer and use it in GitHub Desktop.
Save kyldvs/8a7031514623c17d3f4b to your computer and use it in GitHub Desktop.
FluxDerivedStore
/**
* Copyright 2004-present Facebook. All Rights Reserved.
*
* @providesModule FluxDerivedStore
* @flow
*/
'use strict';
import type Dispatcher from 'Dispatcher';
var FluxStore = require('flux/utils').Store;
var Immutable = require('immutable');
var Set = require('Set');
var abstractMethod = require('abstractMethod');
type InternalKeys = $Enum<{
data: string;
result: string;
}>;
/**
* A derived store is meant to store the result of an expensive computation on
* data that exists in other stores. For example if you have a number store that
* stores some numbers, then you might also want a factor store that stores the
* prime factors of each of those numbers.
*
* @type K type of the keys in this store
* @type TData type of the data that will be used to make the computation on
* @type TResult type of the result of the expensive computation, this is
* returned from the primary getter of this store
*/
class FluxDerivedStore<K, TData, TResult> extends FluxStore {
_cache: Immutable.Map<K, Immutable.Map<InternalKeys, TData | TResult>>;
_stores: Array<FluxStore>;
_tokens: Array<string>;
constructor(dispatcher: Dispatcher) {
super(dispatcher);
this._cache = Immutable.Map();
this._stores = this.__getStores();
this._tokens = this._stores.map(store => store.getDispatchToken());
}
__onDispatch(payload: Object): void {
this.getDispatcher().waitFor(this._tokens);
if (this._stores.some(store => store.hasChanged())) {
this.__emitChange();
}
}
// protected methods you must override
/**
* This method specifies the stores from which your data is derived
*/
__getStores(): Array<FluxStore> {
return abstractMethod('FluxDerivedStore', '__getStores');
}
/**
* This method gets all of the data needed to compute the result. It should
* generally be a really cheap method to run. Do not do any expensive
* computations here.
*/
__getData(key: K): TData {
return abstractMethod('FluxDerivedStore', '__getData');
}
/**
* This computes the result based on the key, data, and previous result.
* Computations here are allowed to be expensive. The computations will only
* be re-run if their underlying data changes.
*
* This method must be pure in order for the derived store to be correct.
* Given the same inputs this method must always produce the same output.
*
* For example using Date.now() will make this method non-pure and break the
* derived store.
*/
__computeResult(key: K, data: TData): TResult {
return abstractMethod('FluxDerivedStore', '__computeResult');
}
// optional methods to override
/**
* Checks if two pieces of data are equal. This is used to determine if we
* should re-run the expensive computation in __computeResult.
*
* If youre computation in __computeResult is actually very cheap and the
* derived store is for convenience rather than performance it is reasonable
* to simply return false here.
*/
__areEqual(one: TData, two: TData): boolean {
return one === two;
}
// public API
get(key: K): TResult {
var currData = this._cache.getIn([key, 'data']);
var nextData = this.__getData(key);
// check if we need to recompute the result
if (!currData || !this.__areEqual(currData, nextData)) {
var nextResult = this.__computeResult(key, nextData);
this._cache = this._cache.withMutations((cache => {
cache.setIn([key, 'data'], nextData);
cache.setIn([key, 'result'], nextResult);
}));
}
// this always will have been set to __computeResult(...) at this point
return this._cache.getIn([key, 'result']);
}
getAll(
keys: Iterable<K>,
prev?: ?Immutable.Map<K, TResult>,
): Immutable.Map<K, TResult> {
var newKeys = new Set(keys);
var start = prev || Immutable.Map();
return start.withMutations((map) => {
start.forEach((value, oldKey) => {
if (!newKeys.has(oldKey)) {
map.delete(oldKey);
}
});
newKeys.forEach((key) => {
map.set(key, this.get(key));
});
});
}
}
module.exports = FluxDerivedStore;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment