Skip to content

Commit

Permalink
refactor(mini-rx-store, signal-store, common): use createStore
Browse files Browse the repository at this point in the history
  • Loading branch information
mini-rx committed Oct 15, 2024
1 parent 0ad4719 commit ccf318e
Show file tree
Hide file tree
Showing 16 changed files with 85 additions and 310 deletions.
9 changes: 7 additions & 2 deletions libs/common/src/lib/create-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function createStore<SelectFnType>(selectableState: {

// Listen to the Actions stream and update state
actionSubscription = actions$.subscribe((action) => {
const nextState = getReducerManager().reducer(appState.get() ?? {}, action);
const nextState = getReducerManager().reducer(appState.get(), action);
appState.set(nextState);
});
}
Expand Down Expand Up @@ -121,7 +121,12 @@ export function createStore<SelectFnType>(selectableState: {
}

return {
hasUndoExtension,
set hasUndoExtension(v: boolean) {
hasUndoExtension = v;
},
get hasUndoExtension(): boolean {
return hasUndoExtension;
},
actions$,
dispatch,
appState, // TODO select?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AbstractReduxDevtoolsExtension, Action, AppState } from '@mini-rx/common';
import { actions$, appState } from '../store-core';
import { actions$, storeCore } from '../store-core';
import { Observable } from 'rxjs';

export class ReduxDevtoolsExtension extends AbstractReduxDevtoolsExtension {
Expand All @@ -8,10 +8,10 @@ export class ReduxDevtoolsExtension extends AbstractReduxDevtoolsExtension {
}

readState(): AppState {
return appState.get()!;
return storeCore.appState.get();
}

updateState(state: AppState): void {
appState.set(state);
storeCore.appState.set(state);
}
}
3 changes: 1 addition & 2 deletions libs/mini-rx-store/src/lib/state.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { BehaviorSubject, Observable, pipe } from 'rxjs';
import { filter, map, distinctUntilChanged } from 'rxjs/operators';
import { BehaviorSubject, distinctUntilChanged, filter, map, Observable, pipe } from 'rxjs';

function createSelectFn<StateType>(state$: Observable<StateType>) {
function select(): Observable<StateType>;
Expand Down
148 changes: 4 additions & 144 deletions libs/mini-rx-store/src/lib/store-core.ts
Original file line number Diff line number Diff line change
@@ -1,146 +1,6 @@
import { Subscription } from 'rxjs';
import { createState } from './state';
import {
AppState,
createActionsOnQueue,
createMiniRxActionType,
createReducerManager,
createRxEffectForStore,
ExtensionId,
MetaReducer,
miniRxError,
OperationType,
Reducer,
ReducerManager,
sortExtensions,
StoreConfig,
StoreExtension,
} from '@mini-rx/common';
import { AppState, createRxEffectForStore, createStore } from '@mini-rx/common';

function createStore() {
let hasUndoExtension = false;
let actionSubscription: Subscription | undefined;

// REDUCER MANAGER
let reducerManager: ReducerManager | undefined;

function getReducerManager(): ReducerManager {
if (!reducerManager) {
reducerManager = createReducerManager();
}
return reducerManager;
}

// ACTIONS
const {actions$, dispatch} = createActionsOnQueue();

// APP STATE
const appState = createState<AppState>();

// Wire up the Redux Store: subscribe to the actions stream, calc next state for every action
// Called by `configureStore` and `addReducer`
function initStore(): void {
if (actionSubscription) {
return;
}

// Listen to the Actions stream and update state
actionSubscription = actions$.subscribe((action) => {
const nextState: AppState = getReducerManager().reducer(appState.get() ?? {}, action);
appState.set(nextState);
});
}

function configureStore(config: StoreConfig<AppState> = {}) {
initStore();

if (getReducerManager().hasFeatureReducers()) {
miniRxError(
'`configureStore` detected reducers. Did you instantiate FeatureStores before calling `configureStore`?'
);
}

if (config.metaReducers) {
getReducerManager().addMetaReducers(...config.metaReducers);
}

if (config.extensions) {
sortExtensions(config.extensions).forEach((extension) => addExtension(extension));
}

if (config.reducers) {
getReducerManager().setFeatureReducers(config.reducers);
}

if (config.initialState) {
appState.set(config.initialState);
}

dispatch({type: createMiniRxActionType(OperationType.INIT)});

return {
dispatch,
feature: addFeature,
effect,
select: appState.select
}
}

function addFeature<StateType extends object>(
featureKey: string,
reducer: Reducer<StateType>,
config: {
metaReducers?: MetaReducer<StateType>[];
initialState?: StateType;
} = {}
): void {
initStore();
getReducerManager().addFeatureReducer(
featureKey,
reducer,
config.metaReducers,
config.initialState
);
dispatch({type: createMiniRxActionType(OperationType.INIT, featureKey)});
}

function removeFeature(featureKey: string): void {
getReducerManager().removeFeatureReducer(featureKey);
dispatch({type: createMiniRxActionType(OperationType.DESTROY, featureKey)});
}

const effect = createRxEffectForStore(dispatch);

function addExtension(extension: StoreExtension): void {
const metaReducer: MetaReducer<AppState> | void = extension.init();

if (metaReducer) {
getReducerManager().addMetaReducers(metaReducer);
}

hasUndoExtension = extension.id === ExtensionId.UNDO;
}

// Used for testing
function destroy(): void {
actionSubscription?.unsubscribe();
actionSubscription = undefined;
reducerManager = undefined;
}

return {
hasUndoExtension,
actions$,
dispatch,
appState,
addFeature,
removeFeature,
effect,
addExtension,
configureStore,
destroy
}
}

export const storeCore = createStore();
export const actions$ = storeCore.actions$
export const storeCore = createStore(createState<AppState>({}));
export const actions$ = storeCore.actions$;
export const rxEffect = createRxEffectForStore(storeCore.dispatch);
6 changes: 2 additions & 4 deletions libs/mini-rx-store/src/lib/store.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { Observable } from 'rxjs';
import {
storeCore
} from './store-core';
import { rxEffect, storeCore } from './store-core';
import {
Action,
Reducer,
Expand Down Expand Up @@ -35,7 +33,7 @@ export function configureStore(config: StoreConfig<AppState>): Store | never {
feature: storeCore.addFeature,
select: storeCore.appState.select,
dispatch: storeCore.dispatch,
effect: storeCore.effect
effect: rxEffect,
};
}
miniRxError('`configureStore` was called multiple times.');
Expand Down
8 changes: 4 additions & 4 deletions libs/signal-store/src/lib/component-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
undo,
UpdateStateCallback,
} from '@mini-rx/common';
import { createSelectableSignalState } from './selectable-signal-state';
import { createSelectableSignal, createSelectableWritableSignal } from './selectable-signal-state';
import { ComponentStoreLike } from './models';
import { createRxEffectFn } from './rx-effect';
import { createConnectFn } from './connect';
Expand All @@ -37,9 +37,9 @@ export class ComponentStore<StateType extends object> implements ComponentStoreL

private actionsOnQueue = createActionsOnQueue();

private _state: WritableSignal<StateType> = signal(this.initialState);
private _state = createSelectableWritableSignal(signal(this.initialState));
get state(): StateType {
return this._state();
return this._state.get();
}

private updateState: UpdateStateCallback<StateType> = (
Expand Down Expand Up @@ -78,7 +78,7 @@ export class ComponentStore<StateType extends object> implements ComponentStoreL
setState = createUpdateFn(this.updateState);
connect = createConnectFn(this.updateState);
rxEffect = createRxEffectFn();
select = createSelectableSignalState(this._state).select;
select = this._state.select;

private destroy(): void {
// Dispatch an action really just for logging via LoggerExtension
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import { AbstractReduxDevtoolsExtension, Action, AppState } from '@mini-rx/common';
import { Observable } from 'rxjs';
import { actions$, select, updateAppState } from '../store-core';
import { storeCore } from '../store-core';

export class ReduxDevtoolsExtension extends AbstractReduxDevtoolsExtension {
get actions$(): Observable<Action> {
return actions$;
return storeCore.actions$;
}

readState(): AppState {
const signalState = select();
return signalState();
return storeCore.appState.get();
}

updateState(state: AppState): void {
updateAppState(state);
storeCore.appState.set(state);
}
}
20 changes: 11 additions & 9 deletions libs/signal-store/src/lib/feature-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import {
undo,
UpdateStateCallback,
} from '@mini-rx/common';
import { addFeature, dispatch, hasUndoExtension, removeFeature, select } from './store-core';
import { createSelectableSignalState } from './selectable-signal-state';
import { storeCore } from './store-core';
import { createSelectableSignal } from './selectable-signal-state';
import { ComponentStoreLike } from './models';
import { createRxEffectFn } from './rx-effect';
import { createConnectFn } from './connect';
Expand All @@ -27,7 +27,9 @@ export class FeatureStore<StateType extends object> implements ComponentStoreLik
return this._featureKey;
}

private _state: Signal<StateType> = select((state) => state[this.featureKey]);
private _state: Signal<StateType> = storeCore.appState.select(
(state) => state[this.featureKey]
);
get state(): StateType {
return this._state();
}
Expand All @@ -37,7 +39,7 @@ export class FeatureStore<StateType extends object> implements ComponentStoreLik
operationType: OperationType,
name: string | undefined
): MiniRxAction<StateType> => {
return dispatch({
return storeCore.dispatch({
type: createMiniRxActionType(operationType, this.featureKey, name),
stateOrCallback,
featureId: this.featureId,
Expand All @@ -48,7 +50,7 @@ export class FeatureStore<StateType extends object> implements ComponentStoreLik
this.featureId = generateId();
this._featureKey = generateFeatureKey(featureKey, config.multi);

addFeature<StateType>(
storeCore.addFeature<StateType>(
this._featureKey,
createFeatureStoreReducer(this.featureId, initialState)
);
Expand All @@ -57,18 +59,18 @@ export class FeatureStore<StateType extends object> implements ComponentStoreLik
}

undo(action: Action): void {
hasUndoExtension
? dispatch(undo(action))
storeCore.hasUndoExtension
? storeCore.dispatch(undo(action))
: miniRxError('UndoExtension is not initialized.');
}

setState = createUpdateFn(this.updateState);
connect = createConnectFn(this.updateState);
rxEffect = createRxEffectFn();
select = createSelectableSignalState(this._state).select;
select = createSelectableSignal(this._state).select;

private destroy(): void {
removeFeature(this._featureKey);
storeCore.removeFeature(this._featureKey);
}
}

Expand Down
6 changes: 3 additions & 3 deletions libs/signal-store/src/lib/modules/store.module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { inject, ModuleWithProviders, NgModule } from '@angular/core';
import { Actions, AppState, FeatureConfig, Reducer, StoreConfig } from '@mini-rx/common';
import { Store } from '../store';
import { actions$, addFeature } from '../store-core';
import { storeCore } from '../store-core';
import {
FEATURE_CONFIGS,
FEATURE_NAMES,
Expand All @@ -27,7 +27,7 @@ export class StoreFeatureModule {
const configs: FeatureConfig<any>[] = inject(FEATURE_CONFIGS);

featureNames.forEach((featureName, index) => {
addFeature(featureName, reducers[index], configs[index]);
storeCore.addFeature(featureName, reducers[index], configs[index]);
});
}
}
Expand All @@ -46,7 +46,7 @@ export class StoreModule {
},
{
provide: Actions,
useValue: actions$,
useValue: storeCore.actions$,
},
],
};
Expand Down
6 changes: 3 additions & 3 deletions libs/signal-store/src/lib/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
Reducer,
StoreConfig,
} from '@mini-rx/common';
import { actions$, addFeature, rxEffect } from './store-core';
import { storeCore, rxEffect } from './store-core';
import { Store } from './store';
import { globalCsConfig } from './component-store';
import {
Expand Down Expand Up @@ -54,7 +54,7 @@ export function provideStore<T>(config: StoreConfig<T>): EnvironmentProviders {
},
{
provide: Actions,
useValue: actions$,
useValue: storeCore.actions$,
},
{ provide: STORE_PROVIDER, useFactory: rootStoreProviderFactory },
{
Expand All @@ -74,7 +74,7 @@ function featureProviderFactory(): void {
const configs = inject(FEATURE_CONFIGS);

featureNames.forEach((featureName, index) => {
addFeature(featureName, reducers[index], configs[index]);
storeCore.addFeature(featureName, reducers[index], configs[index]);
});
}

Expand Down
Loading

0 comments on commit ccf318e

Please sign in to comment.