Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFR] Rely on meta to handle optimistic actions #2684

Merged
merged 4 commits into from
Jan 2, 2019

Conversation

djhi
Copy link
Collaborator

@djhi djhi commented Dec 20, 2018

Context: https://marmelab.com/react-admin/Actions.html#using-a-custom-action-creator

We were relying on the action type to handle optimistic effects. This prevent custom actions to be handled optimistically too.

Consider the following case: I have an ACCEPT_CHANGE and REJECT_CHANGE actions. Both will remove their targeted item from the change resource but will be caught by a custom saga to be applied on another resource. From the change resource perspective, they should be removed after being either accepted or rejected.

export const acceptChange = (
    id: string,
    previousData: any,
    basePath: string
) => ({
    type: ACCEPT_CHANGE,
    payload: { id, previousData },
    meta: {
        resource: 'changes',
        fetch: DELETE,
        onSuccess: {
            notification: {
                body: 'myapp.notification.accepted',
                level: 'info',
                messageArgs: {
                    smart_count: 1,
                },
            },
            redirectTo: 'list',
            basePath,
        },
        onFailure: {
            notification: {
                body: 'ra.notification.http_error',
                level: 'warning',
            },
        },
    },
});

We were relying on the action type to handle optimistic effects. This prevent custom actions to be handled optimistically too.
@djhi djhi added this to the 2.5.3 milestone Dec 20, 2018
}
if (meta.fetch === UPDATE_MANY) {
const updatedRecords = payload.ids
.reduce((records, id) => records.concat(previousState[id]), [])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless previousState[id] returns an array you can use a map here. And in this case you can do it in a single map

.map(id => ({
    ...previousState[id],
    ...payload.data,
}));

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the reduce has another purpose: avoid updating non-existing ids.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even so, it makes no difference. We create a new array any way from the ids in the payload.
And we will always merge the previousState with the data from the payloads, so we might as well do it in a single map.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed

packages/ra-core/src/reducer/admin/resource/data.js Outdated Show resolved Hide resolved
packages/ra-core/src/reducer/admin/resource/data.js Outdated Show resolved Hide resolved
packages/ra-core/src/reducer/admin/resource/data.spec.js Outdated Show resolved Hide resolved
packages/ra-core/src/reducer/admin/resource/data.spec.js Outdated Show resolved Hide resolved
@fzaninotto
Copy link
Member

It took me some time to understand the purpose of this PR so let me summarize my findings here.

The undo saga catches every UNDOABLE action and dispatches the inner action, modified as follows:

  • the name is changed to '[original_name]_OPTIMISTIC'
  • the optimistic: true meta is added.
  • other modifications not relevant to this issue

For instance, for an undoable crudDelete action, the saga dispatches the following action:

// Original action
{
    type: CRUD_DELETE,
    payload: { id, previousData },
    meta: {
        resource,
        fetch: DELETE,
        onSuccess: {
            notification: {
                body: 'ra.notification.deleted',
                level: 'info',
                messageArgs: {
                    smart_count: 1,
                },
            },
            redirectTo,
            basePath,
        },
        onFailure: {
            notification: {
                body: 'ra.notification.http_error',
                level: 'warning',
            },
        },
    },
}

// dispatched action
{
    type: CRUD_DELETE_OPTIMISTIC,
    payload: { id, previousData },
    meta: {
        optimistic: true,
        resource,
        fetch: DELETE,
        notification: {
            body: 'ra.notification.deleted',
            level: 'info',
            messageArgs: {
                smart_count: 1,
            },
        },
        redirectTo,
        basePath,
    },
}

This is fine.

The problem lies in the data reducer, which catches optimistic actions to modify the store immediately (without waiting for the server response). And it catches them by name, i.e. it will react to CRUD_DELETE_OPTIMISTIC.

This prevents usage of custom optimistic actions relying on the same fetch metas, for instance the ACCEPT_CHANGE action described by @djhi. Once redispatched by the undo saga, this action will become ACCEPT_CHANGE_OPTIMISTIC, and will be ignored by the data reducer. This is a pity, since this action is basically a CRUD_UPDATE with a special name.

So the proposal is to change the data reducer to react not on the optimistic action names anymore, but on the optimistic meta instead. that way, the data reducer would react to the ACCEPT_CHANGE_OPTIMISTIC action, not because of its name, but because it's optimistic.

I'm 👍 for that change.

}
if (meta.fetch === UPDATE_MANY) {
const updatedRecords = payload.ids
.reduce((records, id) => records.concat(previousState[id]), [])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the reduce has another purpose: avoid updating non-existing ids.

packages/ra-core/src/reducer/admin/resource/data.js Outdated Show resolved Hide resolved
packages/ra-core/src/reducer/admin/resource/data.js Outdated Show resolved Hide resolved
@ThieryMichel ThieryMichel merged commit 26080c0 into master Jan 2, 2019
@ThieryMichel ThieryMichel deleted the fix-optimistic-reducers branch January 2, 2019 10:12
djhi added a commit that referenced this pull request Jan 11, 2019
fzaninotto added a commit that referenced this pull request Jan 11, 2019
[RFR] Fix incomplete optimistic handling from #2684
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants