feat: add support for subscriptions
This commit is contained in:
parent
86dd272603
commit
9c45ee7efc
20
README.md
20
README.md
@ -1,4 +1,3 @@
|
||||
|
||||
# What's Updux?
|
||||
|
||||
So, I'm a fan of [Redux](https://redux.js.org). Two days ago I discovered
|
||||
@ -16,17 +15,16 @@ is the fun in that? And I'm also having a strong love for
|
||||
All that to say, say hello to `Updux`. Heavily inspired by `rematch`, but twisted
|
||||
to work with `updeep` and to fit my peculiar needs. It offers features such as
|
||||
|
||||
* Mimic the way VueX has mutations (reducer reactions to specific actions) and
|
||||
- Mimic the way VueX has mutations (reducer reactions to specific actions) and
|
||||
effects (middleware reacting to actions that can be asynchronous and/or
|
||||
have side-effects), so everything pertaining to a store are all defined
|
||||
in the space place.
|
||||
* Automatically gather all actions used by the updux's effects and mutations,
|
||||
- Automatically gather all actions used by the updux's effects and mutations,
|
||||
and makes then accessible as attributes to the `dispatch` object of the
|
||||
store.
|
||||
* Mutations have a signature that is friendly to Updux and Immer.
|
||||
* Also, the mutation signature auto-unwrap the payload of the actions for you.
|
||||
* TypeScript types.
|
||||
|
||||
- Mutations have a signature that is friendly to Updux and Immer.
|
||||
- Also, the mutation signature auto-unwrap the payload of the actions for you.
|
||||
- TypeScript types.
|
||||
|
||||
Fair warning: this package is still very new, probably very buggy,
|
||||
definitively very badly documented, and very subject to changes. Caveat
|
||||
@ -78,6 +76,8 @@ store.dispatch.inc(3);
|
||||
# Description
|
||||
|
||||
Full documentation can be [found here](https://yanick.github.io/updux/).
|
||||
Right now the best way to understand the whole thing is to go
|
||||
through the [tutorial](https://yanick.github.io/updux/#/tutorial)
|
||||
|
||||
## Exporting upduxes
|
||||
|
||||
@ -94,7 +94,6 @@ const updux = new Updux({ ... });
|
||||
export default updux;
|
||||
```
|
||||
|
||||
|
||||
Then you can use them as subduxes like this:
|
||||
|
||||
```
|
||||
@ -196,7 +195,6 @@ const updux = new Updux({
|
||||
|
||||
Converting it to Immer would look like:
|
||||
|
||||
|
||||
```
|
||||
import Updux from 'updux';
|
||||
import { produce } from 'Immer';
|
||||
@ -213,7 +211,6 @@ const updux = new Updux({
|
||||
But since typing `produce` over and over is no fun, `groomMutations`
|
||||
can be used to wrap all mutations with it:
|
||||
|
||||
|
||||
```
|
||||
import Updux from 'updux';
|
||||
import { produce } from 'Immer';
|
||||
@ -227,6 +224,3 @@ const updux = new Updux({
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -101,7 +101,6 @@ For TypeScript projects I recommend declaring the actions as part of the
|
||||
configuration passed to the constructors, as it makes them accessible to the class
|
||||
at compile-time, and allows Updux to auto-add them to its aggregated `actions` type.
|
||||
|
||||
|
||||
```
|
||||
const todosUpdux = new Updux({
|
||||
actions: {
|
||||
@ -210,7 +209,6 @@ todosUpdux.addMutation( '*', (payload,action) => state => {
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## Effects
|
||||
|
||||
In addition to mutations, Updux also provides action-specific middleware, here
|
||||
@ -498,7 +496,7 @@ todosUpdux.addMutation( add_todo_with_id, payload =>
|
||||
export default updux.asDux;
|
||||
```
|
||||
|
||||
Note the special '*' subdux key used here. This
|
||||
Note the special '\*' subdux key used here. This
|
||||
allows the updux to map every item present in its
|
||||
state to a `todo` updux. See [this recipe](/recipes?id=mapping-a-mutation-to-all-values-of-a-state) for details.
|
||||
We could also have written the updux as:
|
||||
@ -519,11 +517,9 @@ const updux = new Updux({
|
||||
```
|
||||
|
||||
Note how we are using the `upreducer` accessor in the first case (which yields
|
||||
a reducer for the dux using the signature `(payload,action) => state =>
|
||||
new_state`) and `reducer` in the second case (which yield an equivalent
|
||||
a reducer for the dux using the signature `(payload,action) => state => new_state`) and `reducer` in the second case (which yield an equivalent
|
||||
reducer using the classic signature `(state,action) => new_state`).
|
||||
|
||||
|
||||
### Main store
|
||||
|
||||
```
|
||||
@ -573,6 +569,50 @@ at the main level is actually defined as:
|
||||
const getNextId = state => next_id.selectors.getNextId(state.next_id);
|
||||
```
|
||||
|
||||
## Subscriptions
|
||||
|
||||
Subscriptions can be added by default to a updux store via the initial config
|
||||
or the method `addSubscription`. The signature of a subscription is:
|
||||
|
||||
```
|
||||
(store) => (state,unsubscribe) => {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Subscriptions registered for an updux and its subduxes are automatically
|
||||
subscribed to the store when calling `createStore`.
|
||||
|
||||
The `state` passed to the subscriptions of the subduxes is the local state.
|
||||
|
||||
Also, all subscriptions are wrapped such that they are called only if the
|
||||
local `state` changed since their last invocation.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
const set_nbr_todos = action('set_nbr_todos', payload() );
|
||||
|
||||
const todos = dux({
|
||||
initial: [],
|
||||
subscriptions: [
|
||||
({dispatch}) => todos => dispatch(set_nbr_todos(todos.length))
|
||||
],
|
||||
});
|
||||
|
||||
const myDux = dux({
|
||||
initial: {
|
||||
nbr_todos: 0
|
||||
},
|
||||
subduxes: {
|
||||
todos,
|
||||
},
|
||||
mutations: [
|
||||
[ set_nbr_todos, nbr_todos => u({nbr_todos}) ]
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
## Exporting upduxes
|
||||
|
||||
As a general rule, don't directly export your upduxes, but rather use the accessor `asDux`.
|
||||
|
@ -43,7 +43,9 @@
|
||||
"scripts": {
|
||||
"docsify:serve": "docsify serve docs",
|
||||
"build": "tsc",
|
||||
"test": "tap src/**test.ts"
|
||||
"test": "tap src/**test.ts",
|
||||
"lint": "prettier -c --",
|
||||
"lint:fix": "prettier --write --"
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"repository": {
|
||||
|
107
src/subscriptions.test.ts
Normal file
107
src/subscriptions.test.ts
Normal file
@ -0,0 +1,107 @@
|
||||
import tap from 'tap';
|
||||
import Updux from '.';
|
||||
import { action, payload } from 'ts-action';
|
||||
import u from 'updeep';
|
||||
|
||||
const inc = action('inc');
|
||||
const set_double = action('set_double', payload<number>());
|
||||
|
||||
const dux = new Updux({
|
||||
initial: {
|
||||
x: 0,
|
||||
double: 0,
|
||||
},
|
||||
actions: {
|
||||
inc,
|
||||
},
|
||||
mutations: [
|
||||
[inc, payload => u({ x: x => x + 1 })],
|
||||
[set_double, double => u({ double })],
|
||||
],
|
||||
});
|
||||
|
||||
dux.addSubscription(store => (state, unsubscribe) => {
|
||||
if (state.x > 2) return unsubscribe();
|
||||
|
||||
store.dispatch(set_double(state.x * 2));
|
||||
});
|
||||
|
||||
const store = dux.createStore();
|
||||
|
||||
store.dispatch(inc());
|
||||
|
||||
tap.same(store.getState(), { x: 1, double: 2 });
|
||||
|
||||
store.dispatch(inc());
|
||||
store.dispatch(inc());
|
||||
|
||||
tap.same(store.getState(), { x: 3, double: 4 }, 'we unsubscribed');
|
||||
|
||||
tap.test('subduxes subscriptions', async t => {
|
||||
const inc_top = action('inc_top');
|
||||
const inc_bar = action('inc_bar');
|
||||
const transform_bar = action('transform_bar', payload());
|
||||
|
||||
const bar = new Updux({
|
||||
initial: 'a',
|
||||
mutations: [
|
||||
[inc_bar, () => state => state + 'a'],
|
||||
[transform_bar, outcome => () => outcome],
|
||||
],
|
||||
subscriptions: [
|
||||
store => (state, unsubscribe) => {
|
||||
console.log({ state });
|
||||
|
||||
if (state.length <= 2) return;
|
||||
unsubscribe();
|
||||
store.dispatch(transform_bar('look at ' + state));
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const dux = new Updux({
|
||||
initial: {
|
||||
count: 0,
|
||||
},
|
||||
subduxes: {
|
||||
bar: bar.asDux,
|
||||
},
|
||||
mutations: [[inc_top, () => u({ count: count => count + 1 })]],
|
||||
effects: [
|
||||
[
|
||||
'*',
|
||||
() => next => action => {
|
||||
console.log('before ', action.type);
|
||||
next(action);
|
||||
console.log({ action });
|
||||
},
|
||||
],
|
||||
],
|
||||
subscriptions: [
|
||||
store => {
|
||||
let previous: any;
|
||||
return ({ count }) => {
|
||||
if (count !== previous) {
|
||||
previous = count;
|
||||
store.dispatch(inc_bar());
|
||||
}
|
||||
};
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const store = dux.createStore();
|
||||
|
||||
store.dispatch(inc_top());
|
||||
store.dispatch(inc_top());
|
||||
|
||||
t.same(store.getState(), {
|
||||
count: 2,
|
||||
bar: 'look at look at aaa',
|
||||
});
|
||||
store.dispatch(inc_top());
|
||||
t.same(store.getState(), {
|
||||
count: 3,
|
||||
bar: 'look at look at aaaa',
|
||||
});
|
||||
});
|
109
src/types.ts
109
src/types.ts
@ -37,69 +37,84 @@ export type UnionToIntersection<U> = (U extends any
|
||||
|
||||
export type StateOf<D> = D extends { initial: infer I } ? I : unknown;
|
||||
|
||||
export type DuxStateCoduxes<C> = C extends Array<infer U> ? UnionToIntersection<StateOf<U>>: unknown
|
||||
export type DuxStateSubduxes<C> =
|
||||
C extends { '*': infer I } ? {
|
||||
[ key: string ]: StateOf<I>,
|
||||
[ index: number ]: StateOf<I>,
|
||||
} :
|
||||
C extends object ? { [ K in keyof C]: StateOf<C[K]>}: unknown;
|
||||
export type DuxStateCoduxes<C> = C extends Array<infer U>
|
||||
? UnionToIntersection<StateOf<U>>
|
||||
: unknown;
|
||||
export type DuxStateSubduxes<C> = C extends { '*': infer I }
|
||||
? {
|
||||
[key: string]: StateOf<I>;
|
||||
[index: number]: StateOf<I>;
|
||||
}
|
||||
: C extends object
|
||||
? { [K in keyof C]: StateOf<C[K]> }
|
||||
: unknown;
|
||||
|
||||
type DuxStateGlobSub<S> = S extends { '*': infer I } ? StateOf<I> : unknown;
|
||||
|
||||
type LocalDuxState<S> = S extends never[] ? unknown[] : S;
|
||||
|
||||
/** @ignore */
|
||||
type AggDuxState2<L,S,C> = (
|
||||
L extends never[] ? Array<DuxStateGlobSub<S>> : L & DuxStateSubduxes<S> ) & DuxStateCoduxes<C>;
|
||||
type AggDuxState2<L, S, C> = (L extends never[]
|
||||
? Array<DuxStateGlobSub<S>>
|
||||
: L & DuxStateSubduxes<S>) &
|
||||
DuxStateCoduxes<C>;
|
||||
|
||||
/** @ignore */
|
||||
export type AggDuxState<O,S extends UpduxConfig> = unknown extends O ?
|
||||
AggDuxState2<S['initial'],S['subduxes'],S['coduxes']> : O
|
||||
|
||||
export type AggDuxState<O, S extends UpduxConfig> = unknown extends O
|
||||
? AggDuxState2<S['initial'], S['subduxes'], S['coduxes']>
|
||||
: O;
|
||||
|
||||
type SelectorsOf<C> = C extends { selectors: infer S } ? S : unknown;
|
||||
|
||||
/** @ignore */
|
||||
export type DuxSelectorsSubduxes<C> = C extends object ? UnionToIntersection<SelectorsOf<C[keyof C]>> : unknown;
|
||||
export type DuxSelectorsSubduxes<C> = C extends object
|
||||
? UnionToIntersection<SelectorsOf<C[keyof C]>>
|
||||
: unknown;
|
||||
|
||||
/** @ignore */
|
||||
export type DuxSelectorsCoduxes<C> = C extends Array<infer U> ? UnionToIntersection<SelectorsOf<U>> : unknown;
|
||||
export type DuxSelectorsCoduxes<C> = C extends Array<infer U>
|
||||
? UnionToIntersection<SelectorsOf<U>>
|
||||
: unknown;
|
||||
|
||||
type MaybeReturnType<X> = X extends (...args: any) => any ? ReturnType<X> : unknown;
|
||||
type MaybeReturnType<X> = X extends (...args: any) => any
|
||||
? ReturnType<X>
|
||||
: unknown;
|
||||
|
||||
type RebaseSelector<S, X> = {
|
||||
[ K in keyof X]: (state: S) => MaybeReturnType< X[K] >
|
||||
}
|
||||
[K in keyof X]: (state: S) => MaybeReturnType<X[K]>;
|
||||
};
|
||||
|
||||
type ActionsOf<C> = C extends { actions: infer A } ? A : {};
|
||||
|
||||
type DuxActionsSubduxes<C> = C extends object ? ActionsOf<C[keyof C]> : unknown;
|
||||
export type DuxActionsCoduxes<C> = C extends Array<infer I> ? UnionToIntersection<ActionsOf<I>> : {};
|
||||
export type DuxActionsCoduxes<C> = C extends Array<infer I>
|
||||
? UnionToIntersection<ActionsOf<I>>
|
||||
: {};
|
||||
|
||||
type ItemsOf<C> = C extends object? C[keyof C] : unknown
|
||||
type ItemsOf<C> = C extends object ? C[keyof C] : unknown;
|
||||
|
||||
export type DuxActions<A,C extends UpduxConfig> = A extends object ? A: (
|
||||
UnionToIntersection<ActionsOf<C|ItemsOf<C['subduxes']>|ItemsOf<C['coduxes']>>>
|
||||
);
|
||||
export type DuxActions<A, C extends UpduxConfig> = A extends object
|
||||
? A
|
||||
: UnionToIntersection<
|
||||
ActionsOf<C | ItemsOf<C['subduxes']> | ItemsOf<C['coduxes']>>
|
||||
>;
|
||||
|
||||
export type DuxSelectors<S,X,C extends UpduxConfig> = unknown extends X ? (
|
||||
RebaseSelector<S,
|
||||
C['selectors'] & DuxSelectorsCoduxes<C['coduxes']> &
|
||||
DuxSelectorsSubduxes<C['subduxes']> >
|
||||
): X
|
||||
export type DuxSelectors<S, X, C extends UpduxConfig> = unknown extends X
|
||||
? RebaseSelector<
|
||||
S,
|
||||
C['selectors'] &
|
||||
DuxSelectorsCoduxes<C['coduxes']> &
|
||||
DuxSelectorsSubduxes<C['subduxes']>
|
||||
>
|
||||
: X;
|
||||
|
||||
export type Dux<
|
||||
S = unknown,
|
||||
A = unknown,
|
||||
X = unknown,
|
||||
C = unknown,
|
||||
> = {
|
||||
subduxes: Dictionary<Dux>,
|
||||
coduxes: Dux[],
|
||||
initial: AggDuxState<S,C>,
|
||||
actions: A,
|
||||
}
|
||||
export type Dux<S = unknown, A = unknown, X = unknown, C = unknown> = {
|
||||
subduxes: Dictionary<Dux>;
|
||||
coduxes: Dux[];
|
||||
initial: AggDuxState<S, C>;
|
||||
actions: A;
|
||||
subscriptions: Function[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Configuration object given to Updux's constructor.
|
||||
@ -333,17 +348,17 @@ export type Dux<
|
||||
* ```
|
||||
*/
|
||||
export type UpduxConfig = Partial<{
|
||||
initial: unknown, /** foo */
|
||||
subduxes: Dictionary<Dux>,
|
||||
coduxes: Dux[],
|
||||
actions: Dictionary<ActionCreator>,
|
||||
selectors: Dictionary<Selector>,
|
||||
mutations: any,
|
||||
groomMutations: (m: Mutation) => Mutation,
|
||||
effects: any,
|
||||
initial: unknown /** foo */;
|
||||
subduxes: Dictionary<Dux>;
|
||||
coduxes: Dux[];
|
||||
actions: Dictionary<ActionCreator>;
|
||||
selectors: Dictionary<Selector>;
|
||||
mutations: any;
|
||||
groomMutations: (m: Mutation) => Mutation;
|
||||
effects: any;
|
||||
subscriptions: Function[];
|
||||
}>;
|
||||
|
||||
|
||||
export type Upreducer<S = any> = (action: Action) => (state: S) => S;
|
||||
|
||||
/** @ignore */
|
||||
|
122
src/updux.ts
122
src/updux.ts
@ -51,6 +51,39 @@ type StoreWithDispatchActions<
|
||||
dispatch: { [type in keyof Actions]: (...args: any) => void };
|
||||
};
|
||||
|
||||
function wrap_subscription(sub) {
|
||||
return store => {
|
||||
const sub_curried = sub(store);
|
||||
let previous: unknown;
|
||||
|
||||
return (state, unsubscribe) => {
|
||||
if (state === previous) return;
|
||||
previous = state;
|
||||
|
||||
return sub_curried(state, unsubscribe);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function _subscribeToStore(store: any, subscriptions: Function[] = []) {
|
||||
subscriptions.forEach(sub => {
|
||||
const subscriber = sub(store);
|
||||
let unsub = store.subscribe(() => {
|
||||
const state = store.getState();
|
||||
return subscriber(state, unsub);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function sliced_subscription(slice, sub) {
|
||||
return store => {
|
||||
const sub_curried = sub(store);
|
||||
|
||||
return (state, unsubscribe) =>
|
||||
sub_curried(fp.get(slice, state), unsubscribe);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* `Updux` is a way to minimize and simplify the boilerplate associated with the
|
||||
@ -81,6 +114,8 @@ export class Updux<
|
||||
Mutation<S> | [Mutation<S>, boolean | undefined]
|
||||
> = {};
|
||||
|
||||
private localSubscriptions: Function[] = [];
|
||||
|
||||
get initial(): AggDuxState<S, C> {
|
||||
return buildInitial(
|
||||
this.localInitial,
|
||||
@ -99,30 +134,33 @@ export class Updux<
|
||||
this.coduxes = config.coduxes ?? [];
|
||||
this.subduxes = config.subduxes ?? {};
|
||||
|
||||
Object.entries(config.actions ?? {}).forEach((args) =>
|
||||
Object.entries(config.actions ?? {}).forEach(args =>
|
||||
(this.addAction as any)(...args)
|
||||
);
|
||||
|
||||
this.coduxes.forEach((c: any) =>
|
||||
Object.entries(c.actions).forEach((args) =>
|
||||
Object.entries(c.actions).forEach(args =>
|
||||
(this.addAction as any)(...args)
|
||||
)
|
||||
);
|
||||
Object.values(this.subduxes).forEach((c: any) => {
|
||||
Object.entries(c.actions).forEach((args) => {
|
||||
Object.entries(c.actions).forEach(args => {
|
||||
(this.addAction as any)(...args);
|
||||
});
|
||||
});
|
||||
|
||||
this.groomMutations =
|
||||
config.groomMutations ?? ((x: Mutation<S>) => x);
|
||||
if (config.subscriptions) {
|
||||
config.subscriptions.forEach(sub => this.addSubscription(sub));
|
||||
}
|
||||
|
||||
this.groomMutations = config.groomMutations ?? ((x: Mutation<S>) => x);
|
||||
|
||||
let effects = config.effects ?? [];
|
||||
|
||||
if (!Array.isArray(effects)) {
|
||||
effects = (Object.entries(effects) as unknown) as Effect[];
|
||||
}
|
||||
effects.forEach((effect) => (this.addEffect as any)(...effect));
|
||||
effects.forEach(effect => (this.addEffect as any)(...effect));
|
||||
|
||||
let mutations = config.mutations ?? [];
|
||||
|
||||
@ -130,7 +168,7 @@ export class Updux<
|
||||
mutations = fp.toPairs(mutations);
|
||||
}
|
||||
|
||||
mutations.forEach((args) => (this.addMutation as any)(...args));
|
||||
mutations.forEach(args => (this.addMutation as any)(...args));
|
||||
|
||||
/*
|
||||
|
||||
@ -172,7 +210,7 @@ export class Updux<
|
||||
const selectors = this.selectors;
|
||||
const actions = this.actions;
|
||||
return buildMiddleware(
|
||||
this.localEffects.map((effect) =>
|
||||
this.localEffects.map(effect =>
|
||||
effectToMw(effect, actions as any, selectors as any)
|
||||
),
|
||||
(this.coduxes as any).map(fp.get('middleware')) as any,
|
||||
@ -204,10 +242,7 @@ export class Updux<
|
||||
}
|
||||
|
||||
get upreducer(): Upreducer<S> {
|
||||
return buildUpreducer(
|
||||
this.initial,
|
||||
this.mutations as any
|
||||
) as any;
|
||||
return buildUpreducer(this.initial, this.mutations as any) as any;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -254,10 +289,7 @@ export class Updux<
|
||||
*
|
||||
*/
|
||||
get subduxUpreducer() {
|
||||
return buildUpreducer(
|
||||
this.initial,
|
||||
buildMutations({}, this.subduxes)
|
||||
);
|
||||
return buildUpreducer(this.initial, buildMutations({}, this.subduxes));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -278,12 +310,35 @@ export class Updux<
|
||||
*
|
||||
*
|
||||
*/
|
||||
get createStore() {
|
||||
return buildCreateStore<AggDuxState<S, C>, DuxActions<A, C>>(
|
||||
createStore(...args: any) {
|
||||
const store = buildCreateStore<AggDuxState<S, C>, DuxActions<A, C>>(
|
||||
this.reducer as any,
|
||||
this.middleware as any,
|
||||
this.actions
|
||||
)(...args);
|
||||
|
||||
_subscribeToStore(store, this.subscriptions);
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all subscription functions registered for the dux.
|
||||
* Subdux subscriptions are wrapped such that they are getting their
|
||||
* local state. Also all subscriptions are further wrapped such that
|
||||
* they are only called when the local state changed
|
||||
*/
|
||||
get subscriptions() {
|
||||
let subscriptions = ([
|
||||
this.localSubscriptions,
|
||||
Object.entries(this.subduxes).map(([slice, subdux]) => {
|
||||
return subdux.subscriptions.map(sub =>
|
||||
sliced_subscription(slice, sub)
|
||||
);
|
||||
}),
|
||||
] as any).flat(Infinity);
|
||||
|
||||
return subscriptions.map(sub => wrap_subscription(sub));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -305,6 +360,7 @@ export class Updux<
|
||||
* mutations,
|
||||
* initial,
|
||||
* selectors,
|
||||
* subscriptions,
|
||||
* } = myUpdux.asDux;
|
||||
* ```
|
||||
*
|
||||
@ -324,6 +380,7 @@ export class Updux<
|
||||
mutations: this.mutations,
|
||||
initial: this.initial,
|
||||
selectors: this.selectors,
|
||||
subscriptions: this.subscriptions,
|
||||
};
|
||||
}
|
||||
|
||||
@ -358,11 +415,7 @@ export class Updux<
|
||||
mutation: Mutation<S, any>,
|
||||
isSink?: boolean
|
||||
);
|
||||
addMutation<A extends ActionCreator = any>(
|
||||
creator,
|
||||
mutation,
|
||||
isSink
|
||||
) {
|
||||
addMutation<A extends ActionCreator = any>(creator, mutation, isSink) {
|
||||
const c = this.addAction(creator);
|
||||
|
||||
this.localMutations[c.type] = [
|
||||
@ -420,10 +473,7 @@ export class Updux<
|
||||
* baz(); // => { type: 'baz', payload: undefined }
|
||||
* ```
|
||||
*/
|
||||
addAction(
|
||||
theaction: string,
|
||||
transform?: any
|
||||
): ActionCreator<string, any>;
|
||||
addAction(theaction: string, transform?: any): ActionCreator<string, any>;
|
||||
addAction(
|
||||
theaction: string | ActionCreator<any>,
|
||||
transform?: never
|
||||
@ -442,8 +492,7 @@ export class Updux<
|
||||
payload: transform(...args),
|
||||
}));
|
||||
} else {
|
||||
creator =
|
||||
this.localActions[name] ?? action(name, payload());
|
||||
creator = this.localActions[name] ?? action(name, payload());
|
||||
}
|
||||
} else {
|
||||
name = actionIn.type;
|
||||
@ -466,9 +515,7 @@ export class Updux<
|
||||
const groupByOrder = (mws: any) =>
|
||||
fp.groupBy(
|
||||
([, , actionType]: any) =>
|
||||
['^', '$'].includes(actionType)
|
||||
? actionType
|
||||
: 'middle',
|
||||
['^', '$'].includes(actionType) ? actionType : 'middle',
|
||||
mws
|
||||
);
|
||||
|
||||
@ -486,7 +533,7 @@ export class Updux<
|
||||
])(this.subduxes);
|
||||
|
||||
const local = groupByOrder(
|
||||
this.localEffects.map((x) => [this, [], ...x])
|
||||
this.localEffects.map(x => [this, [], ...x])
|
||||
);
|
||||
|
||||
return fp.flatten(
|
||||
@ -497,7 +544,7 @@ export class Updux<
|
||||
subs.middle,
|
||||
subs['$'],
|
||||
local['$'],
|
||||
].filter((x) => x)
|
||||
].filter(x => x)
|
||||
);
|
||||
}
|
||||
|
||||
@ -517,6 +564,13 @@ sub-state already taken care of you).
|
||||
fp.mapValues('selectors', this.subduxes)
|
||||
) as any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a subscription to the dux.
|
||||
*/
|
||||
addSubscription(subscription: Function) {
|
||||
this.localSubscriptions = [...this.localSubscriptions, subscription];
|
||||
}
|
||||
}
|
||||
|
||||
export default Updux;
|
||||
|
Loading…
Reference in New Issue
Block a user