Why submission result doesn't update?

In the component below, it appears that submission does not update when the action does not return anything.
import { action, useSubmission } from "@solidjs/router";
import { Show } from "solid-js";

const addPost = action(async (formData: FormData) => {
const title = formData.get("title") as string;
if (!title || title.length < 4) {
return {
isError: true,
error: "Title must be at least 4 characters",
};
}
console.log("Post added: ", { title });
}, "addPost");

export default function Page() {
const submission = useSubmission(addPost);
return (
<form action={addPost} method="post">
<input name="title" />
<Show when={submission.result?.isError}>
<p>{submission.result?.error}</p>
</Show>
<button>Submit</button>
</form>
);
}
import { action, useSubmission } from "@solidjs/router";
import { Show } from "solid-js";

const addPost = action(async (formData: FormData) => {
const title = formData.get("title") as string;
if (!title || title.length < 4) {
return {
isError: true,
error: "Title must be at least 4 characters",
};
}
console.log("Post added: ", { title });
}, "addPost");

export default function Page() {
const submission = useSubmission(addPost);
return (
<form action={addPost} method="post">
<input name="title" />
<Show when={submission.result?.isError}>
<p>{submission.result?.error}</p>
</Show>
<button>Submit</button>
</form>
);
}
Let's say I enter "12" in the input field and submit the form. In this case, submission.result would be:
{
isError: true,
error: "Title must be at least 4 characters",
}
{
isError: true,
error: "Title must be at least 4 characters",
}
After that, I input "34" into the field, which passes the validation. I expected that since the action does not return anything in this scenario, submission.result should be undefined and the error message should not be displayed. However, it seems that submission.result does not update at all. I must return something from the action to update the submission result. Can you explain why this happens? I couldn't find any documentation about it.
6 Replies
peerreynders
peerreynders5w ago
I recall that when an action completes without a result that the submission record is simply cleared from the global submissions queue (for garbage collection). The absence of a no-result submission record in the submissions queue conveys that the action has successfully completed. So you may be holding a reference to the submission record after it has already been ejected from the submissions queue.
GitHub
solid-router/src/data/action.ts at 1c9eb8ed2cb70e4fa5a53d0d2836fc11...
A universal router for Solid inspired by Ember and React Router - solidjs/solid-router
peerreynders
peerreynders5w ago
In fact, I think that the second valid action will have it's own, separate submission record. The first failed one is retained as it does carry information to be returned, waiting for you to clear it. So it's not the record isn't updated, the second valid submission has already come and gone and you are looking at the last submission with data. So you should get into the habit on clearing submissions that carry data (error or result) once you've retained the necessary information in order to not be confused by stale entries later on.
Amir Hossein Hashemi
Thank you @peerreynders for your informative response.
So you should get into the habit on clearing submissions that carry data (error or result) once you've retained the necessary information in order to not be confused by stale entries later on.
Can you please explain how this should be done in my example code? I'm aware of the clear method, but I don't know the best practice on where to call it.
peerreynders
peerreynders4w ago
I'd approach it by removing the action from the form and use an onSubmit handler instead (don't forget e.preventDefault()). - collect the form data from the form/event for use as args. - clear the most recent submission if it has an error (or data) - At the end of the handler call the function that's created with const submitAdd = useAction(addAction).
Amir Hossein Hashemi
This way [I think] we lose progressive enhancement and it's a lot of code compared to using action. Maybe a better approach would be to always return something from the action if the result is used:
const addPost = action(async (formData: FormData) => {
const title = formData.get("title") as string;
if (!title || title.length < 4) {
return {
isError: true,
error: "Title must be at least 4 characters",
};
}
return { isError: false }; // <- added this
}, "addPost");
const addPost = action(async (formData: FormData) => {
const title = formData.get("title") as string;
if (!title || title.length < 4) {
return {
isError: true,
error: "Title must be at least 4 characters",
};
}
return { isError: false }; // <- added this
}, "addPost");
peerreynders
peerreynders4w ago
The trade off is that the submission records just keep piling up until the next navigation when submissions are cleared; thats OK if the route has a relatively short session length. Another possibility is to use an effect for cleanup of all the entries before the last. Something like this may work:
const submissions = useSubmissions(addPost);
const submission = () => submissions[submissions.length - 1];

createEffect(() => {
batch(() => {
// purge completed records before last
for (let i = submissions.length - 2; i >= 0; i -= 1) {
const record = submissions[i];
if (!record.pending) record.clear();
}
});
});
const submissions = useSubmissions(addPost);
const submission = () => submissions[submissions.length - 1];

createEffect(() => {
batch(() => {
// purge completed records before last
for (let i = submissions.length - 2; i >= 0; i -= 1) {
const record = submissions[i];
if (!record.pending) record.clear();
}
});
});
FYI: I suspect that this effect will run twice: - once when a new submission is made and the previous one is cleaned up. - a second time because the effect modified submissions when cleaning up. As there is nothing to do, it won't modify anything this time around. Using createEffect to modify values inside the reactive graph is sometimes warranted (often there are better ways to do it) but has to be carefully considered in order to prevent infinite reactive cycles.

Did you find this page helpful?