Created
August 14, 2015 16:04
-
-
Save kyldvs/8a7031514623c17d3f4b to your computer and use it in GitHub Desktop.
FluxDerivedStore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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