-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Error: cannot clone body after it is used #43
Comments
Why do you want to clone the data? I've been running into the same problem a couple of times and manage to extract whatever logic I expected to have done in the action, to the mutation itself :) |
I want to use two forms on one page. I tried to give each form a different action target I also tried to use the So my last idea was to call the mutation based on a hidden field. something like this: import { ActionFunction, json } from "@remix-run/server-runtime"
import { InputError, makeDomainFunction } from "remix-domains"
import { Form, formAction } from "remix-forms"
import { z } from "zod"
const emailSchema = z.object({
type: z.string().nonempty(),
email: z.string().nonempty().email(),
})
const takenEmails = ["foo@bar.com", "bar@foo.com"]
const emailMutation = makeDomainFunction(emailSchema)(async (values) => {
if (takenEmails.includes(values.email)) {
throw new InputError("Email already taken", "email")
}
return values
})
const passwordSchema = z.object({
type: z.string().nonempty(),
password: z.string().nonempty(),
confirmPassword: z.string().nonempty(),
})
const passwordMutation = makeDomainFunction(passwordSchema)(async (values) => {
if (values.password !== values.confirmPassword) {
throw new InputError("Password does not match", "confirmPassword")
}
return values
})
export const action: ActionFunction = async ({ request }) => {
const formData = await request.formData()
let type = formData.get("type")
switch (type) {
case "CHANGE_EMAIL":
return formAction({
request,
schema: passwordSchema,
mutation: passwordMutation,
})
case "CHANGE_PASSWORD":
return formAction({
request,
schema: emailSchema,
mutation: emailMutation,
})
default:
return json("type missing", 400)
}
}
export default () => (
<div>
<Form schema={emailSchema} hiddenFields={["type"]} values={{ type: "CHANGE_EMAIL" }} />
<Form schema={passwordSchema} hiddenFields={["type"]} values={{ type: "CHANGE_PASSWORD" }} />
</div>
) |
If you really want these in the route, I would suggest collecting the schema into one. This doesn't align with seperation of concerns so, but since you want to do two separate actions on the same page. I think this could be a solution. Let me know what you think :) const schema = z.object({
type: z.enum(["CHANGE_EMAIL", "CHANGE_PASSWORD"]),
email: z.string().nonempty(),
password: z.string().nonempty(),
confirmPassword: z.string().nonempty(),
});
const mutation = makeDomainFunction(schema)(async (values) => {
switch (values.type) {
case "CHANGE_EMAIL":
// do email stuff
case "CHANGE_PASSWORD":
// do password stuff
default:
return json("type missing", 400);
}
});
export const action: ActionFunction = async ({ request }) =>
formAction({
mutation,
schema,
request,
});
export default () => (
<div>
<Form schema={schema} values={{ type: "CHANGE_EMAIL" }} />
</div>
); |
Regarding the issue title, try with |
I tried your strategy of using the same action for both forms. I went past the cloning error, but there's something else happening because when I submit one form, the other one also gets client-side validation. In the meantime, the strategy of using different actions for each form is working and it's the one we use and recommend :) Do you want to dig into the errors you had using separate actions? |
I'm closing this since it's a known behaviour of |
Remix throws an error
Error: cannot clone body after it is used
if I try to access the formData before calling the formAction methodconst formData = await request.formData()
Here is a code to reproduce it:
The text was updated successfully, but these errors were encountered: