React vs API interactions improvements

Hi everyone,

I’d like to highlight a PR that is being worked on by @amirfefer. We are trying to standardize update of data on client side after mutation (create, update, destroy) has been perforemed on server side.

With current approach data fetched from API (usually index action of the resource) are basically read-only, but after some mutation has been done on the server side, we need to update this data to see the change in the UI, as of now the only way to do so is to refetch all the data.

After brief discussion [1] we’ve decided to aim to having as simple basic use cases as possible, to reduce boiler plate and offer basic mutations, that will be expected to respond with appropriate data, that should be replaced. This replacement will be done automatically by the middleware and the API response should be responsible for containing all the necessary data (show endpoint should not be missing any data that are included in index endpoint, what is mostly the case for all our endpoints).

If you require a different API data manipulation, you’d need to add your own custom reducers and actions.

I’m sharing this mostly to draw attention of developers (and plugin maintainers), but if you have ideas how we can handle this better, please share your thoughts.

The PR for update action:

[1] Disscussion recording: https://drive.google.com/file/d/1YcinYZ_1HN_YUGaQrPjb4oq8BQ9Ysxzv/view

Thanks @ezr-ondrej for raising this up,

I updated the PR, the UPDATE action is part of the APIMiddleware, consumers can update the corresponding resources list just after after a successful API call:

// This action creates a post API call and only if it ends succesfully,
// the response object will update with the given 'updatedData' object

dispatch(
  post({
    key: MY_SPECIAL_KEY,
    url,
    updatedData: {results: ['altered resource']}
  })
);
1 Like

The basics of this make sense to me, I think I’m on board. If the backend API response comes back with SUCCESS, I think that’s enough to update our copy of the data on the front end. There’s no reason we should have to wait for another GET call to come back and confirm the SUCCESS that we already got. :+1:

Thanks @amirfefer & @ezr-ondrej !
can you share an example of where should it be used?
I would love to see some POC before continuing,

usually handling a local React state after you got the response worked well,
changing it on the Redux API feels a bit of a hack to me,
as it represents the data from the server…

there can be an issue if two components are relying on the same API state response, and one had modified it.

you could also catch the action event in your own reducer,
for example, if the key you pass to the API middleware is HELLO_WORLD
then after success, the middleware will trigger action with type: HELLO_WORLD_SUCCESS,
if you catch it in your own reducer, then you can modify it as much as you want

I got a feeling that we will need it in very rare situations,
but if you can share some commit/code/area that you think it will be awesome!

Thanks, @Laviro, hopefully, my answers clear this issue better

can you share an example of where should it be used?
I would love to see some POC before continuing,

Sure, the need for this behavior raised up during the host statuses feature in the new host details page, a PR is already is up, but I need to update it for the latest changes.

usually handling a local React state after you got the response worked well,
changing it on the Redux API feels a bit of a hack to me,
as it represents the data from the server…

Using a local state for raw data from the server forces us to maintain a local state for multiple components separately. Having a global store is a huge benefit, it keeps the data as one source of truth.
I agree that the store should represent the server, this change still keeps it because the modification occurs only if the API call succeeded.

there can be an issue if two components are relying on the same API state response, and one had modified it.

I’m not sure if I understand the issue you described, if a component modified a resource, the data should be updated for other components as well.
For example, if a user deletes a host status from one component, the data should be consistent with other components.

you could also catch the action event in your own reducer,
for example, if the key you pass to the API middleware is HELLO_WORLD
then after success, the middleware will trigger action with type: HELLO_WORLD_SUCCESS,
if you catch it in your own reducer, then you can modify it as much as you want

A custom reducer can modify only its specific part, i.e - the API reducer can update only the API key object in our store. hence, with this approach, the raw data is duplicated (once in the out-dated API reducer, the other in the custom made reducer), My concern that is data duplication breaks the “One source of truth” of the store.

I got a feeling that we will need it in very rare situations,
but if you can share some commit/code/area that you think it will be awesome!

This change is needed for modifying resources so we won’t need to create a second API call for fetching the updated data from the server.

Is this one the PR: Fixes #31444 - Add host's statuses to the details page by amirfefer · Pull Request #8285 · theforeman/foreman · GitHub ?

If I understand the issue correctly,
you get the statuses list, and in case the user wants to hide some of them, you want them to be removed also from the redux store? although on the server-side they will still exist.

My concern is that if another component wants to know also about one of the statuses now that it’s available, and it’s suddenly gone…

maybe if we will add another key in the API store. e.g:

{
  key: 'HOST_STATUSES_OPTIONS',
  response: {...} // The original response,
  status: 'SUCCESS',
  updatedState: {...} // <--------- Maybe this can be our playground 
}

with the updatedState section you won’t touch the original values, so it will be safer I think,
and there you can place values that structured to your needs, which will result in a simpler state.

I understand your concerns and therefore the corresponding confusion,
to be clear - this change is only for altering the store after a successful modification API call (via post / delete / put - i.e - delete a status), and not for side-client-only changes (i.e - hide a status), this allows us to skip a redundant GET request after a SUCCESS for fetching the updated list from the server after a successful API call.

This change allows a single API middleware call to update the server and the store, so the redux store continue to be consistent with the server.

2 Likes

Thanks for the clarification @amirfefer !!
if we don’t get a response (other than a status) from a POST request for example,
I agree it makes sense to update the response the way you do it :+1:
although it can still not be 100% identical to the server data.

In cases where we can return the freshly updated data after a POST, I would prefer that,
so the response will contain exactly what the server has.