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

access store from effects in WithRedux #35

Open
Chudroy opened this issue Apr 18, 2024 · 7 comments
Open

access store from effects in WithRedux #35

Chudroy opened this issue Apr 18, 2024 · 7 comments
Assignees
Labels
question Further information is requested

Comments

@Chudroy
Copy link

Chudroy commented Apr 18, 2024

as of right now i'm not seeing a way to access state of a signal store from effects in using WithRedux. Would it be possible to add a parameter to effects to make the store accessible? something like

effects(actions, create, store) { 
...
}
@rainerhahnekamp
Copy link
Collaborator

@Chudroy I'm not sure if that's technically possible. We are within a signalFeature and have, therefore, no access to the final store.

Could you describe your use case maybe so that I get a better understanding?

@rainerhahnekamp rainerhahnekamp self-assigned this Apr 19, 2024
@rainerhahnekamp rainerhahnekamp added the question Further information is requested label Apr 19, 2024
@Chudroy
Copy link
Author

Chudroy commented Apr 22, 2024

hi, sure! for example, I used to have a withMethods in my signal store, that looked like this

...
  withMethods(
    (
      store,
      campaignsService = inject(CampaignsService),
      ...
    ) => {
...

and a function within this that was like so:

const loadMachineData = rxMethod<{
        page: number;
        pageSize: number;
        order?: { field: string; order: string };
      }>(
        pipe(
          tap(() => patchState(store, { isLoading: true })),
          switchMap(({ page, pageSize, order }) => {
            return campaignsService
              .getMachineData(
                store.selectedMachine(),
                store.selectedCampaign(),
                page,
                pageSize,
                order,
              )
              .pipe(
                tapResponse(
                  (response) => {
                  ....
                  },
                  (error: HttpErrorResponse) => {
                    ...
                  },
                ),
              );
          }),
        ),
      );

Basically, what I was able to achieve here was call loadMachineDatawithout having to pass in a machine or campaign as an arugment, because i could retrieve store.selectedMachine(), and store.selectedCampaign(), directly from the store's state.

Now that i've refactored my signal store to use withRedux, in my effects, I can't access the store, so i have to select those pieces of state in the component, and pass them as arguments to the action that activates the effect:

in the component:

  loadMachineData(): void {
    const campaign = this.campaignsStore.selectedCampaign();
    const machine = this.campaignsStore.selectedMachine();
    if (!campaign || !machine) return;
    const page = this.paginator.pageIndex + 1;
    const pageSize = this.paginator.pageSize;
    const order = { field: this.sort?.active, order: this.sort?.direction };
    this.campaignsStore.loadMachineData({
      campaign,
      machine,
      page,
      pageSize,
      order,
    });
  }

and in the effect:

        loadMachineData$: create(actions.loadMachineData).pipe(
          switchMap(({ campaign, machine, page, pageSize, order }) => {
            return campaignsService
              .getMachineData(machine, campaign, page, pageSize, order)
              .pipe(
                tapResponse(
                  (response) => {
                    actions.loadMachineDataSuccess({
                      data: response?.data || [],
                      machine,
                      campaign,
                    });
                  },
                  (error: HttpErrorResponse) => {
                    httpErrorSnackbarService.showHttpErrorSnackbar(error);
                    actions.loadMachineDataFailure({ error });
                  },
                ),
              );
          }),
        ),

how i think it could be better:

in component:

  loadMachineData(): void {
    const page = this.paginator.pageIndex + 1;
    const pageSize = this.paginator.pageSize;
    const order = { field: this.sort?.active, order: this.sort?.direction };
    this.campaignsStore.loadMachineData({
      page,
      pageSize,
      order,
    });
  }

adding a store parameter in the function.

 effects(actions, create, store) {
  loadMachineData(): void {
    const page = this.paginator.pageIndex + 1;
    const pageSize = this.paginator.pageSize;
    const order = { field: this.sort?.active, order: this.sort?.direction };
    this.campaignsStore.loadMachineData({
      store.selectedMachine(),
      store.selectedCampaign(),
      page,
      pageSize,
      order,
    });
  }
...
}

On a side-note, I've researched accessing store from effects, and it seems to be a common practice, see https://stackoverflow.com/questions/49177620/bad-practice-to-use-store-in-effect

"Now NgRX (v12) provides its own operator for using the store inside an effect. You don't need the two concatMap, of, withLatestFrom solution anymore. Now you can use concatMapFrom from the @ngrx/effects library like follows: ..."

@ngaritagoitia
Copy link

hi,

any thoughts or news on this issue?

@rainerhahnekamp
Copy link
Collaborator

Yes, we see the requirement for it and will be made available. I'm currently stuck on #44, but maybe this one here should get higher priority.

@rainerhahnekamp
Copy link
Collaborator

A quick update on that one. In order to avoid having too many breaking changes, I'd like to introduce this new feature for the new actions.

We got some feedback from the NgRx team, that we shouldn't have the actions defined in withRedux but - as in the global Store - outside and make it therefore globally available. So other Stores could consume and use these actions in their reducers and effects.

Another side effect will be that the actions will not add methods to the signalStore anymore but need to be dispatched.

We are checking if that requires some kind of Dispatcher service or if an action could be self-dispatchable, meaning you can call it from wherever you want and it will dispatch itself. Alternatively a stand-alone function like dispatch() is also on the table.

In order to get access to the store:

  • The store will provided in withRedux(reduxFn: (store: SignalStore): ReduxData). You see that this is a different signature. Theoretically we could keep the old one for a while but I am not a big fan of that.
  • The reducer function will be a better patchState. You just have to return the partial of the state you want to have changed and patchState will be called internally. Like on(flightsLoaded, (state, ({flights})) => ({...flights}))

@ngaritagoitia, @Chudroy, what is your opinion on that?

@Chudroy
Copy link
Author

Chudroy commented May 12, 2024

Looks good, component store can communicate with global store in a better way, and i understand this also provides access to the store from the effects, so cool. Looking forward to the update!

@w0jcik w0jcik marked this as a duplicate of #146 Feb 13, 2025
@w0jcik
Copy link

w0jcik commented Feb 13, 2025

bumping - looking forward for the update as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

4 participants