conform icon indicating copy to clipboard operation
conform copied to clipboard

Replace `unknown` in submission payload

Open timvandam opened this issue 1 year ago • 5 comments

Fix #628

This pull request replaces the unknown type in submission payloads. unknown is not compatible with Remix Single Fetch and new defineLoader or defineAction as it cannot be guaranteed that values of type unknown can be sent over the wire.

Removing unknown types addresses this issue. I've replaced it with what I think is correct but I don't know the ins and outs of this library so plz check.

Thanks!

timvandam avatar Jul 14 '24 00:07 timvandam

🦋 Changeset detected

Latest commit: e44c9e76db0d5b19211ab3f1ff53833ebd054a0f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@conform-to/dom Patch
@conform-to/react Patch
@conform-to/yup Patch
@conform-to/zod Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

changeset-bot[bot] avatar Jul 14 '24 00:07 changeset-bot[bot]

This does not seem to work for some playground files because File cannot be JSONified, breaking useActionData. While File is never sent from server to client, this is not reflected in the type of SubmissionResult.reply(). What do you think of adding a new reply option that narrows the type to exclude files in the payload type? I think this would make types work with Remix Single Fetch/defineLoader/defineAction using syntax like result.reply({ excludeFiles: true })

Let me know if that works and I will change this PR @edmundhung

timvandam avatar Jul 14 '24 10:07 timvandam

Thanks for the PR @timvandam. I was not aware that I can represent the submission payload this way, but now I see you did that it make total sense :+1:

What do you think of adding a new reply option that narrows the type to exclude files in the payload type? I think this would make types work with Remix Single Fetch/defineLoader/defineAction using syntax like result.reply({ excludeFiles: true })

We are stripping all files already if you are running on the server. So to make it non-breaking, we will need to default the new option to true. But I will just exclude File type on the SubmissionResult for now since only Conform's internal care about the File and it is something I plan to change soon.

I can't push any changes to your PR likely because your PR was based on your main branch. This is what I am thinking:

export type SubmissionPayload<Entry extends FormDataEntryValue> =
	| Entry
	| SubmissionPayload<Entry>[]
	| { [key: string]: SubmissionPayload<Entry> };

export type Submission<Schema, FormError = string[], FormValue = Schema> =
	| {
			status: 'success';
			payload: Record<string, SubmissionPayload<FormDataEntryValue>>;
			value: FormValue;
			reply(options?: ReplyOptions<FormError>): SubmissionResult<FormError>;
	  }
	| {
			status: 'error' | undefined;
			payload: Record<string, SubmissionPayload<FormDataEntryValue>>;
			error: Record<string, FormError | null> | null;
			reply(options?: ReplyOptions<FormError>): SubmissionResult<FormError>;
	  };

export type SubmissionResult<FormError = string[]> = {
	status?: 'error' | 'success';
	intent?: Intent;
	initialValue?: Record<string, SubmissionPayload<string>> | null;
	fields?: string[];
	error?: Record<string, FormError | null>;
	state?: SubmissionState;
};

Then, on replySubmission, we will do this:

const initialValue = normalize(
    context.payload,
    // We can't serialize the file and send it back from the server, but we can preserve it in the client
    typeof document !== 'undefined',
    // We need the file on the client because it's treated as the form value
    // But we will exclude the File type for now as it's only used by the internal
    // form state and we will remove the need to preserve the file on the client soon
) as Record<string, SubmissionPayload<string>> ?? {}

return {
    initialValue,
    // ...
};

edmundhung avatar Aug 09 '24 18:08 edmundhung

Awesome, not sure why you cannot push to my branch, but I've added what you've commented. Very nice solution with the generic, I was thinking of much more complex ways of going about it

timvandam avatar Aug 09 '24 18:08 timvandam

Open in Stackblitz

More templates

@conform-to/dom

pnpm add https://pkg.pr.new/@conform-to/dom@706
@conform-to/react

pnpm add https://pkg.pr.new/@conform-to/react@706
@conform-to/validitystate

pnpm add https://pkg.pr.new/@conform-to/validitystate@706
@conform-to/zod

pnpm add https://pkg.pr.new/@conform-to/zod@706
@conform-to/yup

pnpm add https://pkg.pr.new/@conform-to/yup@706

commit: e44c9e7

pkg-pr-new[bot] avatar Aug 17 '24 13:08 pkg-pr-new[bot]

Hi guys, when will this PR be released, this is very much needed right now for Remix and Single Fetch?

JasonColeyNZ avatar Sep 07 '24 01:09 JasonColeyNZ

I am hoping to cut a release sometimes this month. If you can give the pre-release version a try and let me know whether you run into any issues, it would help me releasing this change sooner. Thanks!

edmundhung avatar Sep 10 '24 17:09 edmundhung

I am hoping to cut a release sometimes this month. If you can give the pre-release version a try and let me know whether you run into any issues, it would help me releasing this change sooner. Thanks!

Sorry to sound dim, how do I do this, do I just change my package .json to ...

    "@conform-to/react": "pre-release",
    "@conform-to/zod": "pre-release",

JasonColeyNZ avatar Sep 10 '24 23:09 JasonColeyNZ

There is a comment above from pkg.pr.new with each package name listed. You will find a command to install the pre-release version of that package if you expand the item.

If you are not using pnpm, you can swap it with npm install, or just copy the package URL and replace the version in the package.json with it.

Really appreciate for giving this a try! 👍

edmundhung avatar Sep 11 '24 06:09 edmundhung