Skip to content

Commit

Permalink
fix: wrap form control in flush sync (#496)
Browse files Browse the repository at this point in the history
  • Loading branch information
edmundhung committed Mar 7, 2024
1 parent 7a19af5 commit f0d899f
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 17 deletions.
12 changes: 8 additions & 4 deletions packages/conform-dom/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@ export type FormOptions<Schema, FormError = string[], FormValue = Schema> = {
submitter: HTMLInputElement | HTMLButtonElement | null;
formData: FormData;
}) => Submission<Schema, FormError, FormValue>;

/**
* To schedule when an intent should be dispatched.
*/
onSchedule?: (callback: () => void) => void;
};

export type SubscriptionSubject = {
Expand Down Expand Up @@ -966,11 +971,10 @@ export function createFormContext<
}

function createFormControl<Type extends Intent['type']>(type: Type) {
const schedule =
latestOptions.onSchedule ?? ((callback: () => void) => callback());
const control = (payload: any = {}) =>
dispatch({
type,
payload,
});
schedule(() => dispatch({ type, payload }));

return Object.assign(control, {
getButtonProps(payload: any = {}) {
Expand Down
9 changes: 7 additions & 2 deletions packages/conform-react/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type FormId, type FieldName } from '@conform-to/dom';
import { useEffect, useId, useState, useLayoutEffect } from 'react';
import { flushSync } from 'react-dom';
import {
type FormMetadata,
type FieldMetadata,
Expand Down Expand Up @@ -49,7 +50,7 @@ export function useForm<
FormError = string[],
>(
options: Pretty<
Omit<FormOptions<Schema, FormError, FormValue>, 'formId'> & {
Omit<FormOptions<Schema, FormError, FormValue>, 'formId' | 'onSchedule'> & {
/**
* The form id. If not provided, a random id will be generated.
*/
Expand All @@ -70,7 +71,11 @@ export function useForm<
const { id, ...formConfig } = options;
const formId = useFormId<Schema, FormError>(id);
const [context] = useState(() =>
createFormContext({ ...formConfig, formId }),
createFormContext({
...formConfig,
onSchedule: (callback: () => void) => flushSync(callback),
formId,
}),
);

useSafeLayoutEffect(() => {
Expand Down
6 changes: 4 additions & 2 deletions packages/conform-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@
"@conform-to/dom": "1.0.2"
},
"devDependencies": {
"@types/react": "^18.2.43",
"@types/react": "^18.2.64",
"@types/react-dom": "^18.2.20",
"react": "^18.2.0"
},
"peerDependencies": {
"react": ">=18"
"react": ">=18",
"react-dom": ">=18"
},
"keywords": [
"constraint-validation",
Expand Down
2 changes: 1 addition & 1 deletion playground/app/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ interface PlaygroundProps {
title: string;
description?: ReactNode;
form?: string;
result?: Record<string, unknown>;
result?: Record<string, unknown> | null | undefined;
formAction?: string;
formMethod?: string;
formEncType?: string;
Expand Down
17 changes: 16 additions & 1 deletion playground/app/routes/form-control.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default function FormControl() {
<FormProvider context={form.context}>
<Form method="post" {...getFormProps(form)}>
<FormStateInput formId={form.id} />
<Playground title="Form Control" result={lastResult}>
<Playground title="Form Control" result={form.value}>
<Field label="Name" meta={fields.name}>
<input {...getInputProps(fields.name, { type: 'text' })} />
</Field>
Expand Down Expand Up @@ -115,6 +115,21 @@ export default function FormControl() {
>
Reset form
</button>
<button
className="rounded-md border p-2 hover:border-black"
type="button"
onClick={() => {
form.update({
name: fields.message.name,
value: 'Hello World',
});
form.reset({
name: fields.number.name,
});
}}
>
Reset number with message updated
</button>
</div>
</Playground>
</Form>
Expand Down
31 changes: 28 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 28 additions & 4 deletions tests/integrations/form-control.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,18 @@ function getFieldset(form: Locator) {
clearMessage: form.locator('button:text("Clear message")'),
resetMessage: form.locator('button:text("Reset message")'),
resetForm: form.locator('button:text("Reset form")'),
resetNumberWithMessageUpdated: form.locator(
'button:text("Reset number with message updated")',
),
};
}

async function runValidationScenario(page: Page) {
async function runTest(
page: Page,
options: {
clientValidate?: boolean;
} = {},
) {
const playground = getPlayground(page);
const fieldset = getFieldset(playground.container);

Expand Down Expand Up @@ -55,17 +63,33 @@ async function runValidationScenario(page: Page) {
await expect(fieldset.number).toHaveValue('');
await expect(fieldset.message).toHaveValue('');
await expect(playground.error).toHaveText(['', '', '']);

if (options.clientValidate) {
await fieldset.number.fill('123');
await expect.poll(playground.result).toStrictEqual({
number: '123',
});

await fieldset.resetNumberWithMessageUpdated.click();
await expect(fieldset.number).toHaveValue('');
await expect(fieldset.message).toHaveValue('Hello World');
await expect.poll(playground.result).toStrictEqual({
message: 'Hello World',
});
}
}

test.describe('With JS', () => {
test('Client Validation', async ({ page }) => {
await page.goto('/form-control');
await runValidationScenario(page);
await runTest(page, {
clientValidate: true,
});
});

test('Server Validation', async ({ page }) => {
await page.goto('/form-control?noClientValidate=yes');
await runValidationScenario(page);
await runTest(page);
});
});

Expand All @@ -74,6 +98,6 @@ test.describe('No JS', () => {

test('Validation', async ({ page }) => {
await page.goto('/form-control');
await runValidationScenario(page);
await runTest(page);
});
});

0 comments on commit f0d899f

Please sign in to comment.