S
SolidJS2w ago
dtnc

Clearing form after action in solid-start

Searches on this discord return old posts that suggest using createEffect on submission.pending along with a ref to the form to do clearing. I'm just checking if this is still the right way to handle form clearing after successful actions. Please let me know if you have a better suggestion. Thanks
5 Replies
peerreynders
peerreynders2w ago
You can specify an onComplete(submission) handler on the action options which could be leveraged in that capacity. I was added for @solidjs/router 0.15.0
GitHub
solid-router/CHANGELOG.md at c05ce351b584a8db2247ddb7863915fdbfc332...
A universal router for Solid inspired by Ember and React Router - solidjs/solid-router
GitHub
solid-router/src/data/action.ts at c05ce351b584a8db2247ddb7863915fd...
A universal router for Solid inspired by Ember and React Router - solidjs/solid-router
dtnc
dtncOP7d ago
thank you so much for taking the time to write thoughtful responses, and with reference links! I tried out action's onComplete and feel that it may better belong with useSubmission. One reason is that having onComplete as a form clean up mechanism essentially requires it to be defined inside a component, thus the action would have to be defined inside the component as well. This makes named actions not reusable across multiple components. Also, if an action is defined inside a component, we can do whatever form clearing or visual update (displaying error messages, success messages, etc.) inside the action async. onComplete doesn't really add value. My 2 cents but I'm still new at this so there is a good chance this perspective is based on incomplete info.
peerreynders
peerreynders6d ago
primitives from which mechanisms can be built”
I swear that needs to be Solid's motto … https://stackblitz.com/edit/stackblitz-starters-9eef3nc1?file=src%2Froutes%2Fabout.tsx,src%2Femail-action.ts
// file: src/email-action.ts
import { createSignal } from 'solid-js';
import { action } from '@solidjs/router';

import { Accessor, Setter } from 'solid-js';

type OnCompleteCallback = NonNullable<
NonNullable<
Parameters<typeof action<[data: FormData], void>>[1]
>['onComplete']
>;

type Listener = {
version: [Accessor<number>, Setter<number>];
onComplete: OnCompleteCallback;
};

const CONTEXT_NAME = 'version';
const listeners = new Set<Listener>();

const onComplete: OnCompleteCallback = (submission) => {
const version = Number(submission.input[0].get(CONTEXT_NAME));
for (const record of listeners) {
if (record.version[0]() !== version) continue;

record.onComplete(submission);
queueMicrotask(() => record.version[1](performance.now()));
break;
}
};

const submitEmail = action(
async (data: FormData) => {
const email = data.get('email');
if (!email) throw new Error('No email specified');
const version = data.get(CONTEXT_NAME);
if (!version)
throw new Error('Did you forget to embed the version signal?');

// DO THE ACTION;
},
{ name: 'submit-action', onComplete }
);

function useSubmitEmail(cb: OnCompleteCallback) {
const version = createSignal(performance.now());
const listener = {
version,
onComplete: cb,
};

const cleanup = () => {
listeners.delete(listener);
};

listeners.add(listener);
return [version[0], { submitEmail, cleanup }] as const;
}

export { useSubmitEmail };
// file: src/email-action.ts
import { createSignal } from 'solid-js';
import { action } from '@solidjs/router';

import { Accessor, Setter } from 'solid-js';

type OnCompleteCallback = NonNullable<
NonNullable<
Parameters<typeof action<[data: FormData], void>>[1]
>['onComplete']
>;

type Listener = {
version: [Accessor<number>, Setter<number>];
onComplete: OnCompleteCallback;
};

const CONTEXT_NAME = 'version';
const listeners = new Set<Listener>();

const onComplete: OnCompleteCallback = (submission) => {
const version = Number(submission.input[0].get(CONTEXT_NAME));
for (const record of listeners) {
if (record.version[0]() !== version) continue;

record.onComplete(submission);
queueMicrotask(() => record.version[1](performance.now()));
break;
}
};

const submitEmail = action(
async (data: FormData) => {
const email = data.get('email');
if (!email) throw new Error('No email specified');
const version = data.get(CONTEXT_NAME);
if (!version)
throw new Error('Did you forget to embed the version signal?');

// DO THE ACTION;
},
{ name: 'submit-action', onComplete }
);

function useSubmitEmail(cb: OnCompleteCallback) {
const version = createSignal(performance.now());
const listener = {
version,
onComplete: cb,
};

const cleanup = () => {
listeners.delete(listener);
};

listeners.add(listener);
return [version[0], { submitEmail, cleanup }] as const;
}

export { useSubmitEmail };
Kent Computing
YouTube
Erlang Master Class 2: Video 2 - Abstracting patterns of concurrency
http://www.cs.kent.ac.uk/ErlangMasterClasses These Master Classes will show you how Erlang can be used in practice to solve larger problems. The examples provide 'capstones' for different aspects of Erlang: functional programming, concurrent programming and larger-scale programming with OTP. Erlang is best known for its “share nothing” con...
StackBlitz
@solidjs/router onComplete - StackBlitz
A Solid TypeScript project based on @solidjs/meta, @solidjs/router, solid-js, typescript, vite and vite-plugin-solid
peerreynders
peerreynders6d ago
// file: src/routes/about.tsx
import { onCleanup, onMount } from 'solid-js';
import { Title } from '@solidjs/meta';
import { useSubmitEmail } from '../email-action';

import type { Submission } from '@solidjs/router';

function About() {
let form: HTMLFormElement | undefined;
const [version, { submitEmail, cleanup }] = useSubmitEmail(
(s: Submission<[data: FormData], void>) => {
const email = s.input[0].get('email');
const version = s.input[0].get('version');
console.log('completed', email, version);

form?.reset();
}
);

onMount(() => {
onCleanup(() => {
cleanup();
});
});

return (
<main>
<Title>About</Title>
<h1>About</h1>
<form ref={form} action={submitEmail} method="post">
<label for="email">email:</label>
<input type="text" name="email" placeholder="[email protected]" />
<input type="hidden" name="version" value={version()} />
<input type="submit" value="submit" />
</form>
</main>
);
}

export { About };
// file: src/routes/about.tsx
import { onCleanup, onMount } from 'solid-js';
import { Title } from '@solidjs/meta';
import { useSubmitEmail } from '../email-action';

import type { Submission } from '@solidjs/router';

function About() {
let form: HTMLFormElement | undefined;
const [version, { submitEmail, cleanup }] = useSubmitEmail(
(s: Submission<[data: FormData], void>) => {
const email = s.input[0].get('email');
const version = s.input[0].get('version');
console.log('completed', email, version);

form?.reset();
}
);

onMount(() => {
onCleanup(() => {
cleanup();
});
});

return (
<main>
<Title>About</Title>
<h1>About</h1>
<form ref={form} action={submitEmail} method="post">
<label for="email">email:</label>
<input type="text" name="email" placeholder="[email protected]" />
<input type="hidden" name="version" value={version()} />
<input type="submit" value="submit" />
</form>
</main>
);
}

export { About };
Of course I'm expecting that you will have to tailor the approach to your particular situation.
dtnc
dtncOP6d ago
thanks. yea I got something going already. Just wanted to contribute some discussion toward slightly better dx real, heavy duty usage probably would involved some form library any way

Did you find this page helpful?