When do you use updater() and patchstate() in ngrx/component-store?

4.9k Views Asked by At

I'm using ngrx/component-store and loving it so far. Having prior store knowledge building my own simple ones, the only real headache I've had so far is when I've had to update an array and figured out I have to always create a new one for the internal compare() pipe to realize the array got updated.

Anyway, reading through the documentation it talks about updater methods and patchState. To me they do exactly the same thing, but their creation is slightly different. You would call patchState inside of a method while this.updater() returns a method giving you a function you can expose in your service. Anytime I'm updating my state it's always after a network call. I assume there are plenty of scenarios where you'd want to update your state without a network call so this is why you would want to have an updater available to your component to call. The question is if an updater and patchState are really doing the same thing then is it a better practice to call an updater in an effect or use patchState, or maybe am I putting too much logic in my effect?

On a side note, the docs say an updater method is supposed to be a pure function. If you're using it to your push an object onto an array then is it really pure?

// adding the selectors so people know what components are subscribing to
readonly approvals$ = this.select(state => state.requestApprovals);
readonly registration$ = this.select(state => state);

readonly updateAssessment = this.effect(($judgement: Observable<{id: string, isApproved: boolean}>) => {
    return $judgement.pipe(
      switchMap((evaluation) => {
        const state = this.get();
        return this.requestApproval.patch(state.id, state.companyName, evaluation.id, evaluation.isApproved).pipe(
          tapResponse(
            (result) => {

             // is it better to call patchState()?
              this.patchState((state) => {
                for(let i = 0; i < state.requestApprovals.length; i++) {
                  if(state.requestApprovals[i].id == result.id) {
                    state.requestApprovals[i].isApproved = result.isApproved;
                  }
                }
                // the take away is you must assign a whole new array object when you update it.
                state.requestApprovals = Object.assign([], state.requestApprovals);
                return state;
              });

              // or this updater?
              // this.applyDecisionPatch(evaluation);
            },
                    // oh look! another updater reassigning my array to the state so
                    // it propagates to subscribers to reset the UI
            () => { this.reverseDecision(); }
          )
        );
      })
    );
  });

// this is private to make sure this can only be called after a network request
  private readonly applyDecisionPatch = this.updater((state, value: {id: string, isApproved: boolean}) => {
    for(let i = 0; i < state.requestApprovals.length; i++) {
      if(state.requestApprovals[i].id == value.id) {
        state.requestApprovals[i].isApproved = value.isApproved;
      }
    }
    state.requestApprovals = Object.assign([], state.requestApprovals);
    return state;
  });

Note: There's no tag for ngrx-component-store so couldn't tag it.

1

There are 1 best solutions below

4
timdeschryver On

An updater can be compared to a reducer.

All the options to modify the state should change it in an immutable way. A library like ngrx-immer can be used to make this easier.

The main difference is that updater receives the current state, and you can change the state based on it. E.g. a conditional update, or can be used with @ngrx/entity

While with setState and patchState, you just set state properties. setState updates the whole state object, whereas patchState only sets the given properties and doesn't touch the rest of the state object. These two methods are also easier to use when you just want to set the state, because you don't have to create an updater function.

To answer the side question, push is not immutable. Instead of creating a new instance, it updates the array instance.