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. This is where state operators come in. The setState method can be passed a state operator which will be used to determine the new state.
Basic
The basic idea of operators 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 finalized using the state slice that they are assigned to.
Example
From theory to practice - let's take the following example:
The 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. In order to understand what this is doing let's express this in a long handed form:
// For demonstration purposes! This long handed form is not needed from NGXS v3.4 onwards. @Action(CreateMonkeys)createMonkeys(ctx: StateContext<AnimalsStateModel>) {conststate=ctx.getState();ctx.setState({...state, monkeys: [] }); }
Supplied State Operators
This is not the only operator, we introduce much more that can be used along with or in place of patch.
If you want to update the value of a property based on some condition - you can use iif, it's signature is:
You will see that in each case above the state operators are wrapped within a call to the patch operator. This is only done because of the convenience that the patch state operator provides for targeting a nested property of the state.
Typing Operators
Specifying types for the patch operator is always necessary when doing nested updates. You can face cases when the patch operator cannot infer the nested type structure. Let's look at the following state:
If we don't specify the type explicitly for patch, all objects are inferred as unknown, meaning that TypeScript cannot tell us that we're doing something wrong or using the wrong type. The correct way of specifying nested types is shown below:
If we change country to Qcountry (intentional mistake), the compiler will tell us Object literal may only specify known properties, but 'Qcountry' does not exist. The same technique may be used with other operators if they cannot infer the type.
💡 Tip: we can specify the state model type and chain properties to get the desired type. Like in the example above.
Custom Operators
You can also define your own operators for updates that are common to your domain. For example:
Here you can see that the developer chose to define a convenience method called addEntity for doing a common state modification. This operator could also have also been defined using existing operators like so:
As you can see, state operators are very powerful to start moving your immutable state updates to be more declarative and expressive. Enhancing the overall maintainability and readability of your state class code.
Snippets
Check this section for more operators that you can add to your application.