NGXS Proposal: Patch operators

Mark Whitfeld
3 min readAug 15, 2018

--

Photo by Victor Aznabaev on Unsplash

Aside: I joined the NGXS project as a team member in April this year and have really enjoyed being involved in such a promising framework. I will be publishing a number of articles detailing some features I am proposing for the framework and welcome any feedback or comments about these.

Background

The NGXS patchState method is used to do immutable object updates to the container state slice without the typical long-handed syntax. This is very neat and convenient because you do not have to use the getState and setState as well as the Object.assign(…)or the spread operator to update the state. The patchState method only offers a shallow patch and as a result is left wanting in more advanced scenarios.

What if it is possible to make the patchState method extensible to create a more advanced yet simple mechanism for state updates?… Let me introduce patch operators…

Proposal

The basic idea of this proposal is that we could describe the modifications to the state using curried functions that are given any inputs that they need to describe the change and are finalised using the state slice that they are assigned to. Hmmm… Maybe that didn’t sound too basic. Let me unpack it a bit.

Given a state tree that looks like this:

let state = {
prop1: ‘Hello’,
prop2: 'World'
};

We can describe an update like this:

let newState = patch({ prop1: 'Goodbye' }, state);

newState will have the following value:

{
prop1: ‘Goodbye’,
prop2: 'World'
}

As an alternative, if we allow the patch function to be curried then we can express the exact same change like this:

let patchFn = patch({ prop1: 'Goodbye' });
let newState = patchFn(state);

For the purposes of this proposal let’s name the patch({...}) call a patch operator. This patch operator expresses the intended modification quite nicely and returns a function that will apply these modifications as a new object based on the provided state. Because we have now separated the expression from the application we can support the creation of an expression ‘tree’ with the patch operators aligned to the properties they should be applied to.

Let’s define the patch operator return value signature:

type op<T> = (state: T) => T;

Now let’s propose a few patch operators:

  • patch<T>(value: T) : op<T>
  • set<T>(value: T) : op<T>
  • patchItem<T>(selector: number | Predicate<T>, operator: op<T>) : op<T[]>
  • removeItem<T>(selector: number | Predicate<T>) : op<T[]>
  • insertItem<T>(value: T, beforePosition? : number) : op<T[]>
  • iif<T>(condition: Predicate<T>, then: op<T>, else: op<T>) : op<T>

This is what a patchState call could look like leveraging these operators:

ctx.patchState({
greeting: ‘Howzit’,
token: patch({
lastUpdate: new Date(),
active: true,
origin: iif( origin => !origin.ip, patch({ ip: ’192.168.1.1’})
}),
roles: [
removeItem(3),
insertItem({id:55, name: ‘Admin’}),
patchItem( x => x.id === 64, {name:’Editor’})
]
});

In this way you can update the data structure by expressing the update in a fashion that mirrors the data structure. My intention is that all of these properties and functions will have type safety. I am hoping that this has become possible with the introduction of conditional types and the infer keyword in Typescript 2.8.

This post just contains a few examples of some patch operators above but I’m sure that there are many other useful operators that we can make. Please add your recommendations for other operators in the issue tracking this proposal:

https://github.com/ngxs/store/issues/545

I have started mocking up some of these operators with tests. Feel free to fork and add another operator (preferably with tests) and then post the link in the comments. Here is my StackBlitz in progress:

--

--

Mark Whitfeld

A Software Developer passionate about writing clean, maintainable, high quality software that delights the user. #TDD #CleanCode #SoftwareCraftsmanship