State
States are classes that define a state container.
Defining a State
States are classes along with decorators to describe metadata and action mappings. To define a state container, let's create an ES2015 class and decorate it with the State
decorator.
In the state decorator, we define some metadata about the state. These options include:
name
: The name of the state slice. Note: The name is a required parameter and must be unique for the entire application.Names must be object property safe, AKA no dashes, dots, etc.
defaults
: Default set of object/array for this state slice.children
: Child sub state associations.
Our states can also participate in dependency injection. This is hooked up automatically so all you need to do is inject your dependencies in the constructor.
Defining Actions
Our states listen to actions via a @Action
decorator. The action decorator accepts an action class or an array of action classes.
Simple Actions
Let's define a state that will listen to a FeedAnimals
action to toggle whether the animals have been fed:
The feedAnimal
function has one argument called StateContext
. This context state has a slice pointer and a function to set the state. It's important to note that the getState()
method will always return the freshest state slice from the global store each time it is accessed. This ensures that when we're performing async operations the state is always fresh. If you want a snapshot, you can always clone the state in the method.
Actions with a payload
Actions can also pass along metadata that has to do with the action. Say we want to pass along how much hay and carrots each zebra needs.
In this example, we have a second argument that represents the action and we destructure it to pull out the name, hay, and carrots which we then update the state with.
There is also a shortcut patchState
function to make updating the state easier. In this case, you only pass it the properties you want to update on the state and it handles the rest. The above function could be reduced to this:
Async Actions
Actions can perform async operations and update the state after an operation.
Typically in Redux your actions are pure functions and you have some other system like a saga or an effect to perform these operations and dispatch another action back to your state to mutate it. There are some reasons for this, but for the most part it can be redundant and just add boilerplate. The great thing here is we give you the flexibility to make that decision yourself based on your requirements.
Let's take a look at a simple async action:
In this example, we reach out to the animal service and call feed
and then call setState
with the result. Remember that we can guarantee that the state is fresh since the state property is a getter back to the current state slice.
You might notice I returned the Observable and just did a tap
. If we return the Observable, the framework will automatically subscribe to it for us, so we don't have to deal with that ourselves. Additionally, if we want the stores dispatch
function to be able to complete only once the operation is completed, we need to return that so it knows that.
Observables are not a requirement, you can use promises too. We could swap that observable chain to look like this:
Dispatching Actions From Actions
If you want your action to dispatch another action, you can use the dispatch
function that is contained in the state context object.
Notice I returned the dispatch function, this goes back to our example above with async operations and the dispatcher subscribing to the result. It is not required though.
Last updated