Last active
June 7, 2024 05:42
-
-
Save baart1989/cd43036f233b747c202985a92bb2ef8e to your computer and use it in GitHub Desktop.
Create Redux Store with redux-persist
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
function reduxProviderFactory(provider: ReduxInitService) { | |
return () => provider.initialise(); | |
} | |
@NgModule({ | |
imports: [], | |
declarations: [], | |
exports: [], | |
providers: [ | |
DevToolsExtension, | |
ObservableStore, | |
ReduxInitService, | |
{ provide: APP_INITIALIZER, useFactory: reduxProviderFactory, deps: [ReduxInitService], multi: true }, | |
... | |
}); |
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
@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; | |
} |
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
/* 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); | |
} | |
} |
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
@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