-
-
Notifications
You must be signed in to change notification settings - Fork 5.3k
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
[RFC] Using hooks for fetch side effects #3413
Comments
I love it 😍 |
Wouldn't it be even more readable to return a query
Then use the |
The query hooks ( |
Okay, I think I understand. |
There is something wrong about the API I suggest, and that's
That means that using a JS I'm still scratching my head looking for a better alternative. |
The best I can come up with is to return functions to call for success and error side effects. // proposed hooks signatures
const { data, total, loading, error, onSuccess, onError } = useQuery({ type, resource, payload });
const [mutate, { data, total, loading, error, onSuccess, onError }] = useMutation({ type, resource, payload }); So the syntax for side effects looks like the following for read queries: // in useEditController.js
// before
const { data: record, loading } = useGetOne(resource, id, {
basePath,
version, // used to force reload
onFailure: {
notification: {
body: 'ra.notification.item_doesnt_exist',
level: 'warning',
},
redirectTo: 'list',
refresh: true,
},
});
// after
const notify = useNotification();
const redirect = useRedirection();
const refresh = useRefresh();
const { data: record, loading, onError } = useGetOne(resource, id, { version })
onError(() => {
notify('ra.notification.item_doesnt_exist', 'warning');
redirect('list', basePath);
refresh();
}) And for mutations: // in ra-core/src/controllers/useCreatecontroller.ts
const notify = useNotification();
const redirect = useRedirection();
const [mutate, { loading: isSaving, onSuccess, onError }] = useMutation(
{ type: 'EDIT', resource, payload: {} } // payload is set later by the caller of mutate
);
const save = (data, redirectTo = 'list') => {
mutate(null, { data });
onSuccess(() => {
notify('ra.notification.created', 'info', {
messageArgs: {
smart_count: 1,
},
});
redirect(redirectTo, basePath);
});
onError(() => {
notify('ra.notification.http_error', 'warning');
});
} |
In fact, the syntax I just proposed above is not really better than another syntax that is closer to the current one, and also allows both hook-based side effects and custom side effects: // in useEditController.js
// before
const { data: record, loading } = useGetOne(resource, id, {
basePath,
version, // used to force reload
onFailure: {
notification: {
body: 'ra.notification.item_doesnt_exist',
level: 'warning',
},
redirectTo: 'list',
refresh: true,
},
});
// after
const notify = useNotification();
const redirect = useRedirection();
const refresh = useRefresh();
const { data: record, loading } = useGetOne(resource, id, {
version, // used to force reload
onFailure: () => {
notify('ra.notification.item_doesnt_exist', 'warning');
redirect('list', basePath);
refresh();
}
}) Straightfoward, isn't it? And for mutations: // in ra-core/src/controllers/useCreatecontroller.ts
const notify = useNotification();
const redirect = useRedirection();
const [mutate, { loading: isSaving, onSuccess, onError }] = useMutation(
{ type: 'EDIT', resource, payload: {} } // payload is set later by the caller of mutate
);
const save = (data, redirectTo = 'list') => {
mutate(null, { data }, {
onSuccess: () => {
notify('ra.notification.created', 'info', {
messageArgs: {
smart_count: 1,
},
});
redirect(redirectTo, basePath);
},
onError: () => {
notify('ra.notification.http_error', 'warning');
}
});
} Implementing this last proposed syntax is much more natural than implementing the previous syntax, which implies modified useQuery to return onSuccess and onError callbacks (sigh)
|
Another possibility to handle the additional configuration, would be to use a configurable function (like in d3). const notify = useNotification();
const redirect = useRedirection();
const refresh = useRefresh();
const { data: record, loading } = useGetOne()
.onFailure(() => {
notify('ra.notification.item_doesnt_exist', 'warning');
redirect('list', basePath);
refresh();
})(resource, id, {
version, // used to force reload
}) This could also make the big configuration object more manageable, as we could add method for each configurable property, not just onSuccess and onFailure. |
Fixed by #3425 |
Problem
We we wrote react-admin, there was no common solution for handling side effects (like redirection, notification, or refresh after the dataProvider answers). So we've chosen redux-saga, and declared side effects as objects in the
meta
field of redux actions, as follows:In v3, Following RFC #2952, we've removed the side effects from the action object to let them be decided by the caller. The current version in
next
looks like this:So we've abstracted the action away (in the
useMutation
hook) but not the side effects.I find these side effect objects to be an unnecessary level of indirection. They are hard to write, they require constant lookups in the documentation for syntax, they are hard to explain. Is the error side effect key 'onError' or 'onFailure'? I always have to check the right syntax.
And React 16.8 now offers a native way to handle side effects: hooks.
Proposed Solution
The
useMutation
hook uses theuseDataProvider
hook. I've designeduseDataProvider
to return a Promise, so that it's easy for the developer to execute code once the call to the data provider ends. But that ability wasn't kept in the currentuseMutation
implementation. If we keep that ability (returning a Promise), then side effects can be added naturally:But then, how to trigger these side effects? Let's create hooks for notification, redirection, and refresh.
Drawback
While the changes to
useMutation
and the implementation of the new hooks aren't very hard, a problem occurs withuseQuery
. That's because this hook does not return a callback asuseMutation
, so we have nothing to attach a Promise to.We need a way to trigger side effects for read queries, too (e.g. redirecting to the
list
page when a request for a record in theedit
page returns a 404).So I suggest we change the signatures of both
useQuery
anduseMutation
to always return the query, which is a Promise:That way, it becomes possible to attach success and failure side effects to read queries, too:
Considering we're still in the alpha phase, I think we can afford changing these signatures.
Benefits
What's your take on that change?
The text was updated successfully, but these errors were encountered: