Support raw-object submissions to actions (bypass Request serialization) #9858
Replies: 8 comments 1 reply
-
You can submit a plain object (non-nested) and it will be serialized into the request form data:
Does that handle your use case? |
Beta Was this translation helpful? Give feedback.
-
Hi, thanks for replying and apolgies for getting back to you so late. I didn't know about that feature so it's good to know as it helps a little. Knowing that I could do the following: import { useForm } from "react-hook-form";
import { useSubmit } from "react-router-dom";
import { set } from "lodash";
export default function App() {
const { register, handleSubmit, formState: { errors } } = useForm();
const submit = useSubmit();
const onSubmit = data => submit(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("name.first")} />
<input {...register("name.last", { required: true })} />
{errors.name.last && <span>This field is required</span>}
<input type="submit" />
</form>
);
};
export const action = async ({ request }) => {
const user = request.formData().entries().reduce((acc, [key, val]) => set(acc, key, val), {});
await API.graphql(graphqlOperation(createUser, { input: user }));
}; The action will accept the key/value formdata object and re-parse it back into a structured object. This approach has some flaws though, e.g. there's a difference in how react-form-hook handles arrays and how lodash set does: Lodash I think parsing the results in and out of formdata is not a nice approach. If it was possible to send a js object directly to the action that would make the scenario of using a graphql API and a form library much better. |
Beta Was this translation helpful? Give feedback.
-
We're pretty happy leveraging standardized web APIs such as import { useForm } from "react-hook-form";
import { useSubmit } from "react-router-dom";
import { set } from "lodash";
export default function App() {
const { register, handleSubmit, formState: { errors } } = useForm();
const submit = useSubmit();
const onSubmit = data => submit({
// You can implement any custom serialization logic here
serialized: JSON.stringify(data)
});
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("name.first")} />
<input {...register("name.last", { required: true })} />
{errors.name.last && <span>This field is required</span>}
<input type="submit" />
</form>
);
};
export const action = async ({ request }) => {
let formData = await request.formData();
// And then just parse your own format here
let user = JSON.parse(formData.get('serialized'));
await API.graphql(graphqlOperation(createUser, { input: user }));
}; |
Beta Was this translation helpful? Give feedback.
-
@brophdawg11 Your solution is only a workaround as serialization is an unnecessary step if we can transfer a reference to an object. It would be better to add the customData parameter to the SubmitFunction export interface SubmitFunction {
(
/**
* Specifies the `<form>` to be submitted to the server, a specific
* `<button>` or `<input type="submit">` to use to submit the form, or some
* arbitrary data to submit.
*
* Note: When using a `<button>` its `name` and `value` will also be
* included in the form data that is submitted.
*/
target: SubmitTarget,
/**
* Options that override the `<form>`'s own attributes. Required when
* submitting arbitrary data without a backing `<form>`.
*/
options?: SubmitOptions
/**
* User data
*/
customData?: any): void;
} and next pass customData as the third parameter to an action: export interface ActionFunctionArgs extends DataFunctionArgs {
customData?: any;
} Like @connelhooley I use graphql mutations (Relay), but my form outputs are typed objects obtained from Formik. The new react-router version with loaders is fantastic, and it's great step forward as we all need render-as-you-fetch. However, the library needs some additional features to be truly useful. |
Beta Was this translation helpful? Give feedback.
-
Could someone please tell why doesn't the example above work for me? Here's it is in a sandbox. https://codesandbox.io/s/nice-pike-zy3hqc?file=/src/index.js I am content if I can just console.log the value inside the action. I can take it from there. Thank you. |
Beta Was this translation helpful? Give feedback.
-
I figure it out. I had to use the fetcher to pass the data to action. import { useForm } from "react-hook-form";
import { useFetcher } from "react-router-dom";
export default function App() {
const fetcher = useFetcher();
const {
register,
handleSubmit,
formState: { errors }
} = useForm();
const onSubmit = (data) =>
fetcher.submit(
{
// You can implement any custom serialization logic here
serialized: JSON.stringify(data)
},
{ method: "post", action: "/" }
);
return (
<fetcher.Form onSubmit={handleSubmit(onSubmit)}>
<input {...register("first")} />
<input {...register("last", { required: true })} />
{errors?.name?.last && <span>This field is required</span>}
<input type="submit" />
</fetcher.Form>
);
}
export const action = async ({ request }) => {
console.log("action called....");
let formData = await request.formData();
// And then just parse your own format here
let user = JSON.parse(formData.get("serialized"));
console.log(user); // <== I am content if this console.log works
return null;
}; |
Beta Was this translation helpful? Give feedback.
-
I'm going to convert this to a discussion so it can go through our new Open Development process. Please upvote the new Proposal if you'd like to see this considered! |
Beta Was this translation helpful? Give feedback.
-
Good news folks! We're going to add support for this and have written up the expected API in a new proposal (#10324), so I'm going to close this out and please refer to the linked proposal for future progress here. We hope to start work on this soon. |
Beta Was this translation helpful? Give feedback.
-
What is the new or updated feature that you are suggesting?
I would like to submit a request via GraphQL when I submit a form. I am using react-hook-form to validate and format the object I want to submit. I know the docs recommend using browser validation but I would prefer to keep react-hook-form if possible. There's an awful lot of stuff it handles for me. E.g. handling repeatable inputs, formatting all the fields into a structured object.
If I could pass an object into an action from my component via the
useSubmit
hook, this would unlock a lot more scenarios forreact-router
imo.E.g. something like this:
Is there a better way to do this without such a feature? I can't see an obvious and nice way to invoke a graphql mutation without ditching react-form-hooks. This would mean I:
Maybe I shouldn't be using actions but my "OCD" is telling me that if I'm using loaders (which work great with GraphQL, there's docs for it), I should be using actions!
Why should this feature be included?
Beta Was this translation helpful? Give feedback.
All reactions