Closed GuillaumeNury closed 1 month ago
Hi @GuillaumeNury,
I agree that we need more types in the public API to support advanced use cases such as this one. However, it's important to mention that this is not the right way of building custom features. We should not reimplement the logic of base features, but use them instead because the internal logic can change in the meantime.
This is how withTabVisibility
feature should look like:
type TabVisibilityMethod<Input extends SignalStoreFeatureResult> = (
store: Prettify<
StateSignals<Input['state']> &
Input['computed'] &
Input['methods'] &
StateSource<Input['state']>
>
) => void;
export function withTabVisibility<
Input extends SignalStoreFeatureResult
>(methods: {
onTabVisible?: TabVisibilityMethod<Input>;
onTabHidden?: TabVisibilityMethod<Input>;
}): SignalStoreFeature<Input, { state: {}; computed: {}; methods: {} }> {
return signalStoreFeature(
type<Input>(),
withHooks((store) => {
const visibilityChangeFn = () => {
document.hidden
? methods.onTabHidden?.(store)
: methods.onTabVisible?.(store);
};
return {
onInit() {
document.addEventListener('visibilitychange', visibilityChangeFn);
},
onDestroy() {
document.removeEventListener('visibilitychange', visibilityChangeFn);
},
};
})
);
}
// Usage:
const CounterStore = signalStore(
withState({ count: 0 }),
withMethods((store) => ({
increment(): void {
patchState(store, { count: store.count() + 1 });
},
decrement(): void {
patchState(store, { count: store.count() - 1 });
},
})),
withTabVisibility({
onTabVisible: ({ increment }) => increment(),
onTabHidden: ({ decrement }) => decrement(),
})
);
The following models are missing from the public API to implement features such as this one:
SignalStoreFeatureResult
StateSignals
Prettify
They will be added to the public API.
@markostanimirovic thanks for your reply!
I cannot find what StateSource
is.
Would it be better to expose SignalStoreFeatureResult
, StateSignals
, Prettify
and StateSource
or SignalStoreFeatureResult
and a new type:
// This type avoid exposing "utils" like Prettify
export type SignalStoreFeatureInputStore<Input extends SignalStoreFeatureResult> = Prettify<
StateSignals<Input['state']> &
Input['computed'] &
Input['methods'] &
StateSource<Input['state']>
>
The previous implementation would become:
type TabVisibilityMethod<Input extends SignalStoreFeatureResult> = (
store: SignalStoreFeatureInputStore<Input>
) => void;
export function withTabVisibility<
Input extends SignalStoreFeatureResult
>(methods: {
onTabVisible?: TabVisibilityMethod<Input>;
onTabHidden?: TabVisibilityMethod<Input>;
}): SignalStoreFeature<Input, { state: {}; computed: {}; methods: {} }> {
return signalStoreFeature(
type<Input>(),
withHooks((store) => {
const visibilityChangeFn = () => {
document.hidden
? methods.onTabHidden?.(store)
: methods.onTabVisible?.(store);
};
return {
onInit() {
document.addEventListener('visibilitychange', visibilityChangeFn);
},
onDestroy() {
document.removeEventListener('visibilitychange', visibilityChangeFn);
},
};
})
);
}
// Usage:
const CounterStore = signalStore(
withState({ count: 0 }),
withMethods((store) => ({
increment(): void {
patchState(store, { count: store.count() + 1 });
},
decrement(): void {
patchState(store, { count: store.count() - 1 });
},
})),
withTabVisibility({
onTabVisible: ({ increment }) => increment(),
onTabHidden: ({ decrement }) => decrement(),
})
);
Anyway, thanks for the type<Input>()
trick!
I cannot find what StateSource is.
StateSignal
will be renamed to StateSource
in v18. https://github.com/ngrx/platform/pull/4424
Would it be better to expose SignalStoreFeatureResult, StateSignals, Prettify and StateSource or SignalStoreFeatureResult and a new type:
It will be useful for features that need access to all store members. However, there may be a case where you want to restrict access and expose e.g. only state and computed signals:
type MyCustomFeatureMethod<Input extends SignalStoreFeatureResult> = (
store: Prettify<StateSignals<Input['state']> & Input['computed']>
) => void;
Therefore, types are separated for better flexibility.
@markostanimirovic Ok, makes sense!
@markostanimirovic EmptyFeatureResult
would be useful too
Which @ngrx/* package(s) are relevant/related to the feature request?
signals
Information
In order to add more complexe features, it would be great to expose more models to the public API.
Here is an example of what I try to achieve
It is possible but I used many unexposed models:
What would be marvelous would be to:
EmptyFeatureResult
andSignalStoreFeatureResult
SignalStoreFeatureInputStore<Input>
(could be used bywithMethods
andwithHooks
)createFeatureInputStore
(could be used bywithMethods
andwithHooks
as well)I could have written:
Thanks for this awesome lib ❤️
Describe any alternatives/workarounds you're currently using
It is possible to add hooks with:
Then:
The
onTabVisible
method seems completely decoupled from thewithTabVisibility
feature.I would be willing to submit a PR to fix this issue