Understanding Volto Actions and Reducers (chaining / side effects?)

I'm looking for some help understanding Volto Actions and Reducers more in-depth. I would really appreciate any general links to documentation or tips, as well as specific answers to my questions below!


Looking at the plone training page about Volto actions (47. Volto Actions and Component State – Mastering Plone 6 Development — Plone Training 2022 documentation) I understand that you can simply construct an Action object with a type and details about a request:

export function getVotes(url) {
  return {
    type: GET_VOTES,
    request: {
      op: 'get',
      path: `${url}/@votes`,
    },
  };
}

When this is dispatched, something is watching for it, makes the request, and then eventually dispatches other actions with types like GET_VOTES_PENDING, GET_VOTES_SUCCESS, GET_VOTES_FAIL.

My first questions are: What is the something that turns request actions into data? Is it a special reducer? Some kind of side-effect handler? Can I ask it to make complicated requests, multiple requests, etc?


In my case I will need to fetch data about instances of a content type that "contains" many other instances of other content types. To fit with the example above, suppose I am building an overview page where I will see a list of elections that have happened over time. I have a plone folder containing all the elections and I make an action getElections which returns them as a list. Now I need to make requests for each election returned in order to get the votes that happened in that election.

I understand that there is a query parameter fullobjects=1 which can help remove the need for these extra requests, but in this case fullobjects would still not return enough.

I also understand that in most cases it would make sense to implement a single backend endpoint which gathers all the data needed.

But I would like to understand how best to accomplish this using Volto actions and reducers.

So my second set of questions: Is there a Volto-way of chaining multiple actions / requests together? If not, is there a pattern or library anyone would recommend for implementing this elegantly? I have a couple of hacky solutions, but I'm starting to think I should research packages like redux-loop which seem to solve the problem more generally... has anyone tried redux-loop already?


Thanks so much for reading this long post! :tada:

Volto uses an ordinary react-redux action dispatcher way of saving data into the Redux store. For multiple requests, you might want to look at "subrequests". It will prevent overriding of the global state associated with a particular action. See: Redux - Volto Developer Documentation
https://github.com/plone/volto/blob/3772e3c971c3eb5e7cdde0bf072f5e660f41fc3c/src/reducers/querystringsearch/querystringsearch.js#L30

1 Like

Q1: And a direct link to the passage in Volto's docs that points to the "api" middleware that does the magic trick

The gist of it is that Volto has a Redux middleware that intercepts all actions. "Actions" are just JS objects with a type field an an optional payload. When the middleware detect that the action has a request field, it treats it as a potential network call. You can do multiple calls by setting the action.request to be an array, and you can set the mode property in the action to be either parallel or serial. That middleware's code is a bit convoluted, as there's many cases to be handled. Maybe at some point it will be rewritten, to make it more legible.

As far as I remember, you can get the api middleware to make calls to any endpoint, not just the Plone backend. Be aware that there's potential CORS headers issues (the destination server needs to send an "OK" that the client's browser can fetch XHR data from the current window location). There's an addon that can use Volto's SSR node server to proxy requests, to avoid the CORS issues.

Q2: set the action.request to be an array and the mode to parallel, or serial

3 Likes

Answering to second question: Volto actions support "redux-thunk" thus, all actions can be converted to a thunk, then chain dispatching actions. Problem is that we don't do that in Vanilla Volto, so I can't point you to any example.

It would be something like:

      dispatch(
        getContent(
          `/foo/bar`,
          null,
          "my-unique-request",
        ),
      ).then((resp) => {
      // dispatch another action, act upon
      });

I hope this helps!

4 Likes

Thank you all very much! I look forward to trying these suggestions soon

Plone Foundation Code of Conduct