-
Notifications
You must be signed in to change notification settings - Fork 6.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
"Middleware" redux state changes, not actions.
- Loading branch information
Showing
3 changed files
with
168 additions
and
1 deletion.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
// @flow | ||
|
||
const logger = require('jitsi-meet-logger').getLogger(__filename); | ||
|
||
/** | ||
* The type listener supported for registration with | ||
* {@link StateListenerRegistry} in association with a {@link Selector}. | ||
* | ||
* @param {any} selection - The value derived from the redux store/state by the | ||
* associated {@code Selector}. Immutable! | ||
* @param {Store} store - The redux store. Provided in case the {@code Listener} | ||
* needs to {@code dispatch} or {@code getState}. The latter is advisable only | ||
* if the {@code Listener} is not to respond to changes to that state. | ||
* @param {any} prevSelection - The value previously derived from the redux | ||
* store/state by the associated {@code Selector}. The {@code Listener} is | ||
* invoked only if {@code prevSelection} and {@code selection} are different. | ||
* Immutable! | ||
*/ | ||
type Listener = (selection: any, store: Store, prevSelection: any) => void; | ||
|
||
/** | ||
* The type selector supported for registration with | ||
* {@link StateListenerRegistry} in association with a {@link Listener}. | ||
* | ||
* @param {Object} state - The redux state from which the {@code Selector} is to | ||
* derive data. | ||
* @param {any} prevSelection - The value previously derived from the redux | ||
* store/state by the {@code Selector}. Provided in case the {@code Selector} | ||
* needs to derive the returned value from the specified {@code state} and | ||
* {@code prevSelection}. Immutable! | ||
* @returns {any} The value derived from the specified {@code state} and/or | ||
* {@code prevSelection}. The associated {@code Listener} will only be invoked | ||
* if the returned value is other than {@code prevSelection}. | ||
*/ | ||
type Selector = (state: Object, prevSelection: any) => any; | ||
|
||
/** | ||
* A type of a {@link Selector}-{@link Listener} association in which the | ||
* {@code Listener} listens to changes in the values derived from a redux | ||
* store/state by the {@code Selector}. | ||
*/ | ||
type SelectorListener = { | ||
|
||
/** | ||
* The {@code Listener} which listens to changes in the values selected by | ||
* {@link selector}. | ||
*/ | ||
listener: Listener, | ||
|
||
/** | ||
* The {@code Selector} which selects values whose changes are listened to | ||
* by {@link listener}. | ||
*/ | ||
selector: Selector | ||
}; | ||
|
||
/** | ||
* A registry listeners which listen to changes in a redux store/state. | ||
*/ | ||
class StateListenerRegistry { | ||
/** | ||
* The {@link Listener}s registered with this {@code StateListenerRegistry} | ||
* to be notified when the values derived by associated {@link Selector}s | ||
* from a redux store/state change. | ||
*/ | ||
_selectorListeners: Set<SelectorListener> = new Set(); | ||
|
||
_listener: (Store) => void; | ||
|
||
/** | ||
* Invoked by a specific redux store any time an action is dispatched, and | ||
* some part of the state (tree) may potentially have changed. | ||
* | ||
* @param {Object} context - The redux store invoking the listener and the | ||
* private state of this {@code StateListenerRegistry} associated with the | ||
* redux store. | ||
* @returns {void} | ||
*/ | ||
_listener({ prevSelections, store }: { | ||
prevSelections: Map<SelectorListener, any>, | ||
store: Store | ||
}) { | ||
for (const selectorListener of this._selectorListeners) { | ||
const prevSelection = prevSelections.get(selectorListener); | ||
|
||
try { | ||
const selection | ||
= selectorListener.selector( | ||
store.getState(), | ||
prevSelection); | ||
|
||
if (prevSelection !== selection) { | ||
prevSelections.set(selectorListener, selection); | ||
selectorListener.listener(selection, store, prevSelection); | ||
} | ||
} catch (e) { | ||
// Don't let one faulty listener prevent other listeners from | ||
// being notified about their associated changes. | ||
logger.error(e); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Registers a specific listener to be notified when the value derived by a | ||
* specific {@code selector} from a redux store/state changes. | ||
* | ||
* @param {Function} selector - The pure {@code Function} of the redux | ||
* store/state (and the previous selection of made by {@code selector}) | ||
* which selects the value listened to by the specified {@code listener}. | ||
* @param {Function} listener - The listener to register with this | ||
* {@code StateListenerRegistry} so that it gets invoked when the value | ||
* returned by the specified {@code selector} changes. | ||
* @returns {void} | ||
*/ | ||
register(selector: Selector, listener: Listener) { | ||
this._selectorListeners.add({ | ||
listener, | ||
selector | ||
}); | ||
} | ||
|
||
/** | ||
* Subscribes to a specific redux store (so that this instance gets notified | ||
* any time an action is dispatched, and some part of the state (tree) of | ||
* the specified redux store may potentially have changed). | ||
* | ||
* @param {Store} store - The redux store to which this | ||
* {@code StateListenerRegistry} is to {@code subscribe}. | ||
* @returns {void} | ||
*/ | ||
subscribe(store: Store) { | ||
// XXX If StateListenerRegistry is not utilized by the app to listen to | ||
// state changes, do not bother subscribing to the store at all. | ||
if (this._selectorListeners.size) { | ||
store.subscribe( | ||
this._listener.bind( | ||
this, | ||
{ | ||
/** | ||
* The previous selections of the {@code Selector}s | ||
* registered with this {@code StateListenerRegistry}. | ||
* | ||
* @type Map<any> | ||
*/ | ||
prevSelections: new Map(), | ||
|
||
/** | ||
* The redux store. | ||
* | ||
* @type Store | ||
*/ | ||
store | ||
})); | ||
} | ||
} | ||
} | ||
|
||
export default new StateListenerRegistry(); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export * from './functions'; | ||
export { default as MiddlewareRegistry } from './MiddlewareRegistry'; | ||
export { default as ReducerRegistry } from './ReducerRegistry'; | ||
export { default as StateListenerRegistry } from './StateListenerRegistry'; |