Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: signalStoreFeature and access to Store #4340

Open
1 of 2 tasks
rainerhahnekamp opened this issue May 14, 2024 · 0 comments
Open
1 of 2 tasks

RFC: signalStoreFeature and access to Store #4340

rainerhahnekamp opened this issue May 14, 2024 · 0 comments

Comments

@rainerhahnekamp
Copy link
Contributor

rainerhahnekamp commented May 14, 2024

Which @ngrx/* package(s) are relevant/related to the feature request?

signals

Information

This is a proposal for connector function which connects a signalStore to a feature and provides the feature access to store-specific elements.

A StackBlitz example is available here: https://stackblitz.com/edit/ngrx-signal-store-starter-wkuxdh


Sometimes, our Signal Store Features require features from the store they should be plugged in. Since features should be re-usable, direct coupling is impossible.

Following use case. A feature requires access to a loading function which is defined in withMethods of the store.

We have the following feature

function withEntityVersioner<Entity>(loader: () => Observable<Entity[]>) {
  return signalStoreFeature(
    withState({ version: 1, entities: new Array<Entity>() }),
    withMethods((store) => {
      return {
        update: rxMethod<unknown>(
          pipe(
            switchMap(() => loader()),
            filter((entities) => entities !== store.entities()),
            tap((entities) =>
              patchState(store, (value) => ({
                entities,
                version: value.version + 1,
              })),
            ),
          ),
        ),
      };
    }),
    withHooks((store) => ({ onInit: () => store.update(interval(1000)) })),
  );
}

And a store which wants to use it:

interface Person {
  firstname: string;
  lastname: string;
}

signalStore(
  withMethods(() => {
    const httpClient = inject(HttpClient);
    return {
      load() {
        return httpClient.get<Person[]>('someUrl');
      },
    };
  }),
  withEntityVersioner(() => store.load()) // does not compile
);

The proposed feature is about a function withFeatureFactory which connects an autonomous feature to a specific store.

signalStore(
  withMethods(() => {
    const httpClient = inject(HttpClient);
    return {
      load() {
        return httpClient.get<Person[]>('someUrl');
      },
    };
  }),
  withFeatureFactory((store) => withEntityVersioner(() => store.load())),
);

The type of withFeatureFactory would look like this:

export declare function withFeatureFactory<
  Input extends SignalStoreFeatureResult,
  Feature extends SignalStoreFeatureResult,
>(
  featureSupplier: (
    store: Prettify<
      SignalStoreSlices<Input['state']> &
        Input['signals'] &
        Input['methods'] &
        StateSignal<Prettify<Input['state']>>
    >,
  ) => SignalStoreFeature<EmptyFeatureResult, Feature>,
): SignalStoreFeature<Input, EmptyFeatureResult & Feature>;

This feature would be helpful in the discussion #4338.

Although, it is different use case, it is very similar to the suggestion of @gabrielguerrero in #4314 (comment)

Describe any alternatives/workarounds you're currently using

The alternative is that the load function in the example would come from a service which the signalStoreFeature has to inject.

I would be willing to submit a PR to fix this issue

  • Yes
  • No
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant