Redux Stores

Redux stores are used to maintain global state

The term "store" in this article refers to a JavaScript object we can use to keep track of state. Not to be confused with Magento stores.

ScandiPWA uses Redux to keep track of global state. If used correctly, Redux is predictable and easy to debug.

Following Redux practices, the ScandiPWA theme contains 1 Redux store. However, since the application needs to maintain different kinds of global state, the top-level Redux store actually tracks an object containing multiple "sub-stores". Each of these sub-stores, has a dedicated subdirectory in store where it is defined.

The use of Redux stores is not ScandiPWA-specific, so it is best to learn about it from the oficial redux documentation. However, we will go though an example of how it is used in ScandiPWA to help you understand how it interacts with the application.

Example: Breadcrumbs

At the top of many pages in the ScandiPWA theme, you will see breadcrumbs. These are path-like indicators that help the user understand where in the app they currently are, and improve navigation:

Since we would never need to keep track of multiple different breacrumb paths at once, they are implemented in a global redux store. Once a component receives enough data to know what the breadcrumbs should be (such as a list of parent categories as above), it dispatches an update to the breadcrumbs. This state can now be read by the component responsible for rendering breadcrumbs.

1. The .reducer File: Defining the State

See store/Breadcrumbs/Breadcrumbs.reducer.js.

The first step in creating a redux store is defining what it's initial state should be. We need to keep track of two things - the breadcrumbs themselves and a boolean indicating wether they should be visible on the current page.

Now, we need to describe how the state should update in response to certain actions. It might seem unintuitive at first, but Redux state cannot be updated directly. Instead, you are allowed to define reducers - functions that describe how the state should transition when an action is dispatched.

We want to handle two types of actions - one that updates the breadcrumbs, and one that updates their visibility:

All actions are simple JavaScript objects that carry information which the reducer can interpret to update the state. The UPDATE_BREADCRUMBS action would look like this:

The TOGGLE_BREADCRUMBS would be similar, but carry a different type of data, a boolean:

Note that, by themselves, actions do not do anything - they are just objects with some fields. However, when an action is dispatched ("sent" to Redux, we'll get to that later), Redux passes it on to all reducers (functions we define). Each reducer can look at the action and update its state by returning a new value.

Now that the reducer is defined, we need to include it in our single global Redux state.

2. The .action File: Defining Possible Actions

The reducer we created can respond to certain actions described above, but creating those action object manually would get repetitive and error-prone. Hence, we create functions that can create these action objects for us:

3. The .dispatcher File: Dispatching Helpers

It can be convenient to have a file that defines helpers for dispatching actions. That's what the .dispatcher file is for:

Unlike the reducer or action creators, you are free to have side effects in the dispatcher. For example, in store/Cart/Cart.dispatcher.js, addProductToCart makes a GraphQl mutation request before updating the store by dispatching a cart data update.

4. Usage in Components

Any component's container can read and dispatch to the Redux state by using the connect higher-order component.

Reading the state: Example

Dispatching to the state: Example

Last updated

Was this helpful?