Provide the default callback to `enhance`
See original GitHub issueDescribe the problem
I’ve seen people try to add a loading state via enhance a couple times. They do something like this:
<form use:enhance={() => {
loading = true;
return () => {
loading = false;
}
}}>
However, they don’t realize that once you return a callback from the function passed to enhance, the default behavior after a form submission no longer applies and you have to do the following yourself:
- run
invalidateAll
if the result was a success - run
applyAction
if the current location matches or the result was a redirect/error
This logic currently happens in the default callback.
This means if you want to extend enhance
, e.g. to add a loading state, you have to duplicate that logic, even if your customizations are minimal:
<form use:enhance={() => {
loading = true;
return ({action, result}) => {
+ if (result.type === 'success') {
+ await invalidateAll();
+ }
+
+ if (
+ location.origin + location.pathname === action.origin + action.pathname ||
+ result.type === 'redirect' ||
+ result.type === 'error'
+ ) {
+ applyAction(result);
+ }
loading = false;
}
}}>
(You could remove the check around applyAction
and always apply it, but I don’t like how that diverges you from the default enhance
behavior, perhaps without even realizing it).
You can abstract this into a custom action that wraps enhance
to reduce the boilerplate, but you won’t get any updates that are made to the default callback behavior, e.g. the changes in https://github.com/sveltejs/kit/pull/6828.
All you wanted to do was toggle a loading state, but now you have to maintain the default enhance
behavior as well.
Of course, there are cases where you want to customize the invalidate or applyAction
behavior. However, I suspect those cases are more the exception than the norm.
Describe the proposed solution
Provide an additional argument to the callback returned from enhance. Name is bikesheddable, but for the purposes of this issue I’ll call it defaultBehavior
. The implementation of this method is the same as fallback_callback. People who want to add some logic after a form submission and preserve the default behavior could call this method instead of recreating the logic themselves. This way, if the default behavior is updated, custom actions calling this method get it for free.
The example above would then look like this:
<form use:enhance={() => {
loading = true;
return ({action, result, defaultBehavior}) => {
defaultBehavior();
loading = false;
}
}}>
If people want to write enhance callbacks that always do the default callback behavior, then they could create their own action that calls defaultBehavior
without having to track any updates to the behavior.
Alternatives considered
One alternative is to keep the status quo and require the user to duplicate the default behavior if desired.
Another alternative is to call fallback_callback
automatically after the returned callback runs, but also pass a cancel
arg to the returned callback that would prevent that behavior if desired.
<form use:enhance={() => {
loading = true;
return ({action, result, cancel}) => {
// invalidate & applyAction called automatically, unless cancel is called
loading = false;
}
}}>
This makes the default case simpler. However, this has a few downsides:
- it’s a breaking change for people returning a callback that didn’t want
invalidateAll
andapplyAction
called - because we need to run the provided callback to find out if
cancel
was called, there isn’t a good way to run code after invalidation & applying the action has completed. SupplyingdefaultBehavior
gives the user control over when invalidation happens.
Importance
would make my life easier
Additional Information
No response
Issue Analytics
- State:
- Created a year ago
- Reactions:10
- Comments:5 (5 by maintainers)
I meant the other
cancel()
. The one specified in this issue in Alternatives considered, which is proposed to be passed to the result callback.We will not rename
cancel
topreventDefault
- this sounds unrelated to this issue.The
done
function is interesting, but what happens if you call it and return a callback function?I think the semantics are more clear and easier to understand with an additional argument to the callback function