StateListenerRegistry
"Middleware" redux state changes, not actions.
This commit is contained in:
parent
8cd2bd272b
commit
db21e97c19
|
@ -10,7 +10,11 @@ import Thunk from 'redux-thunk';
|
||||||
import { i18next } from '../../base/i18n';
|
import { i18next } from '../../base/i18n';
|
||||||
import { localParticipantLeft } from '../../base/participants';
|
import { localParticipantLeft } from '../../base/participants';
|
||||||
import { Fragment, RouteRegistry } from '../../base/react';
|
import { Fragment, RouteRegistry } from '../../base/react';
|
||||||
import { MiddlewareRegistry, ReducerRegistry } from '../../base/redux';
|
import {
|
||||||
|
MiddlewareRegistry,
|
||||||
|
ReducerRegistry,
|
||||||
|
StateListenerRegistry
|
||||||
|
} from '../../base/redux';
|
||||||
import { SoundCollection } from '../../base/sounds';
|
import { SoundCollection } from '../../base/sounds';
|
||||||
import { PersistenceRegistry } from '../../base/storage';
|
import { PersistenceRegistry } from '../../base/storage';
|
||||||
import { toURLString } from '../../base/util';
|
import { toURLString } from '../../base/util';
|
||||||
|
@ -404,6 +408,9 @@ export class AbstractApp extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StateListenerRegistry
|
||||||
|
store && StateListenerRegistry.subscribe(store);
|
||||||
|
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
|
@ -1,3 +1,4 @@
|
||||||
export * from './functions';
|
export * from './functions';
|
||||||
export { default as MiddlewareRegistry } from './MiddlewareRegistry';
|
export { default as MiddlewareRegistry } from './MiddlewareRegistry';
|
||||||
export { default as ReducerRegistry } from './ReducerRegistry';
|
export { default as ReducerRegistry } from './ReducerRegistry';
|
||||||
|
export { default as StateListenerRegistry } from './StateListenerRegistry';
|
||||||
|
|
Loading…
Reference in New Issue