Skip to content

Instantly share code, notes, and snippets.

@osadan
Created March 23, 2022 08:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save osadan/00e20693f1d6c047e4b034d2bab95a19 to your computer and use it in GitHub Desktop.
Save osadan/00e20693f1d6c047e4b034d2bab95a19 to your computer and use it in GitHub Desktop.
one store for multiple package instances
import "regenerator-runtime/runtime";
import { combineReducers, applyMiddleware, createStore,compose } from 'redux';
import createSagaMiddleware from 'redux-saga';
import dynamicMiddlewares from 'redux-dynamic-middlewares';
import { composeWithDevTools } from 'redux-devtools-extension';
import { all } from 'redux-saga/effects';
const defaultConfig = {
reducersToSkipFromClearing: [],
clearActions: []
}
const monitoringReducer = (state = { monitor: [] }, action) => {
switch (action.type) {
case "@STORE/MONITOR":
return { ...state, monitor: monitor.concat(action.payload) };
default: return state
}
}
class Store {
static #instance;
#sagas;
#sagaMiddleware;
#asyncReducers;
store;
dispatch;
constructor() {
this.#sagas = new Map();
this.#sagaMiddleware = createSagaMiddleware({});
this.#asyncReducers = {};
this.store = this.#createStore([this.#sagaMiddleware]);
this.#sagaMiddleware.run(this.initialSaga);
this.dispatch = this.store.dispatch;
this.#createStoreElement();
}
static getInstance() {
if (!Store.instance) {
if (!Store.#storeInitialized()) {
Store.#instance = new Store()
} else {
Store.#instance = Store.#getStoreInstanceFromElement();
}
}
return Store.#instance;
}
static #storeInitialized() {
const [store] = document.getElementsByTagName('store');
if (store && store[Symbol.for('store')]) {
return true;
}
return false;
}
static #getStoreInstanceFromElement() {
const [store] = document.getElementsByTagName('store');
if (store && store[Symbol.for('store')]) {
return store.instance;
}
}
#createStore() {
const composeEnhancers = composeWithDevTools({
// Specify name here, actionsBlacklist, actionsCreators and other options if needed
name: 'freightos-store',
trace: true,
traceLimit: 5,
features: {
jump: false,
},
});
//)
return createStore(
this.#createReducer(),
composeEnhancers(applyMiddleware(dynamicMiddlewares, this.#sagaMiddleware))
);
}
#createStoreElement() {
const storeElement = document.createElement('store');
Object.defineProperty(storeElement, Symbol.for('store'), {
writable: true,
configurable: false,
value: {}
})
Object.defineProperty(storeElement, 'instance', {
writable: true,
configurable: false,
value: this
});
const [head] = document.getElementsByTagName('head');
if (head) {
head.appendChild(storeElement);
}
return storeElement[Symbol.for('store')];
}
* initialSaga(sagas = []) {
yield all(sagas.map(sagaItem => sagaItem()))
}
#createReducer(asyncReducers = {}) {
// @todo think of a way to start without active reducers
return combineReducers({
monitor: monitoringReducer,
...asyncReducers,
});
}
attachReducer(reducers = {}) {
if (!this.store) {
return;
}
// @todo don't add multiple reducers
Object.entries(reducers).forEach(([key, reducer]) => {
this.#asyncReducers[key] = reducer;
});
this.store.replaceReducer(createReducer(this.#asyncReducers));
}
attach(reducers, sagas) {
if (sagas) {
this.injectSaga(sagas);
}
if (reducers) {
this.attachReducer(reducers);
}
};
injectSage(saga) {
if (!this.#sagas) {
return;
}
if (this.#sagas.has(saga.toString())) {
return undefined;
}
this.#sagas.set(saga.toString(), true);
return this.#sagaMiddleware.run(saga);
}
}
export default Store;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment