Skip to content

Instantly share code, notes, and snippets.

@baart1989
Last active June 7, 2024 05:42
Show Gist options
  • Save baart1989/cd43036f233b747c202985a92bb2ef8e to your computer and use it in GitHub Desktop.
Save baart1989/cd43036f233b747c202985a92bb2ef8e to your computer and use it in GitHub Desktop.
Create Redux Store with redux-persist
function reduxProviderFactory(provider: ReduxInitService) {
return () => provider.initialise();
}
@NgModule({
imports: [],
declarations: [],
exports: [],
providers: [
DevToolsExtension,
ObservableStore,
ReduxInitService,
{ provide: APP_INITIALIZER, useFactory: reduxProviderFactory, deps: [ReduxInitService], multi: true },
...
});
@Injectable()
export class DevToolsExtension {
constructor(private appRef: ApplicationRef, private store$: NgReduxStore) {}
/**
* A wrapper for the Chrome Extension Redux DevTools.
*
* @argument options: dev tool options; same
* format as described here:
* [zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md]
*/
enhancer = (options?: Object) => {
let subscription: Function;
if (!this.isEnabled()) {
return null;
}
// Make sure changes from dev tools update angular's view.
environment.devToolsExtension.listen(({ type }: any) => {
if (type === 'START') {
subscription = this.store$.subscribe(() => {
if (!NgZone.isInAngularZone()) {
this.appRef.tick();
}
});
} else if (type === 'STOP') {
subscription();
}
});
return environment.devToolsExtension(options);
};
/**
* Returns true if the extension is installed and enabled.
*/
isEnabled = () => environment && environment.devToolsExtension;
}
/* eslint-disable prettier/prettier */
import { AnyAction, Store } from 'redux';
import { BehaviorSubject, Observable } from 'rxjs';
import { distinctUntilChanged, map } from 'rxjs/operators';
import { AppState, INITIAL_STATE } from 'app/redux/stores/main.store';
import { Injectable } from '@angular/core';
import { NgZone } from '@angular/core';
export type Comparator = (x: any, y: any) => boolean;
/**
* This interface represents the glue that connects the
* subscription-oriented Redux Store with the RXJS Observable-oriented
* Angular component world.
*/
@Injectable({ providedIn: 'root' })
export class ObservableStore {
private reduxStore: Store<AppState>;
private state$: BehaviorSubject<AppState>;
constructor(private ngZone: NgZone) {
this.state$ = new BehaviorSubject<AppState>(INITIAL_STATE);
}
provideStore(store: Store<AppState>) {
this.reduxStore = store;
this.reduxStore.subscribe(() => this.state$.next(this.reduxStore.getState()));
}
/**
* This method can be used to get some part of the redux state synchronously
*/
getState<SelectedType>(selector: (state: AppState) => SelectedType): SelectedType {
return selector(this.state$.value);
}
/**
* This method can be used to get some part of the redux state asynchronously
*/
select<SelectedType>(selector: (state: AppState) => SelectedType, comparator?: Comparator): Observable<SelectedType> {
return this.state$.asObservable().pipe(
map(state => selector(state)),
distinctUntilChanged(comparator),
);
}
dispatch<A extends AnyAction>(action: A) {
if (NgZone.isInAngularZone()) {
this.reduxStore.dispatch(action);
return action;
}
this.ngZone.run(() => this.reduxStore.dispatch(action));
return action;
}
subscribe(listener: () => void) {
return this.reduxStore.subscribe(listener);
}
}
@Injectable({ providedIn: 'root' })
export class ReduxInitService {
private persistor: Persistor;
rootEpics: RootEpics;
ngRedux: NgReduxStore;
devTools: DevToolsExtension;
constructor(private injector: Injector) {}
initialise(): Promise<boolean> {
return new Promise<boolean>(resolve => {
this.devTools = this.injector.get(DevToolsExtension);
this.ngRedux = this.injector.get(NgReduxStore);
this.rootEpics = this.injector.get(RootEpics);
const epicMiddleware = createEpicMiddleware();
const enhancers = environment.production === 'false' && this.devTools.isEnabled() ? [this.devTools.enhancer()] : [];
const logger = environment.production === 'true' ? [] : [loggerMiddleware];
const storageConfig = JnStorage.getStorageConfig();
const rootEpic = this.rootEpics.createEpics();
const middlewares = [...logger, epicMiddleware];
const store = configureStore(middlewares, enhancers, storageConfig);
this.ngRedux.provideStore(store);
epicMiddleware.run(rootEpic);
this.persistor = persistStore(store, undefined, () => {
console.log('Stores are rehydrated with local data.');
resolve(true);
});
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment