Closed eovandodev closed 1 year ago
Hello! Thank you so much for the support, I think the issue you are facing is with the fact how Remix handles file uploads, check this part of the documentation: https://remix.run/docs/en/1.17.1/utils/parse-multipart-form-data The package tries to get all the data from the passed formData but it natively doesn't handle file uploads and I am waiting for the API to become stable to actually add it to the package, what you can do is try getting the data by hand and then passing it into validateFormData, I will in any case investigate this and add a readme update to help clear this up
// You get the parsed file here
const formData = await unstable_parseMultipartFormData(request);
// Then you validate
const { errors, data } =
await validateFormData<FormData>(formData, resolver);
if (errors) {
return json(errors);
}
// Do something with the data
};
Do let me know if this works. Currently sick but I am planning to look into this further when I am feeling a bit better
@AlemTuzlak Thanks for the quick responses. Unfortunately, I wasn't able to solve the problem with just that. I followed the suggested path, but I ended up removing the react-hook-form package to keep things moving forward due to time constraints. I appreciate your help, and I hope this issue can be resolved in the future.
// You get the parsed file here const formData = await unstable_parseMultipartFormData(request); // Then you validate const { errors, data } = await validateFormData<FormData>(formData, resolver); if (errors) { return json(errors); } // Do something with the data };
Do let me know if this works. Currently sick but I am planning to look into this further when I am feeling a bit better
The code you provided is still missing the uploadHandler. Remix provides two utilities to handle this: unstable_createFileUploadHandler and unstable_createMemoryUploadHandler. It is our responsibility to decide which one fits the use case better. However, we can always build our custom uploadHandler, which is what I ended up doing in my case since I'm using Supabase buckets for storage.
Here's an example of a custom uploadHandler implementation:
const uploadHandler = async (part: UploadHandlerPart): Promise<string> =>
new Promise(async (resolve, reject) => {
try {
// Handle the file here
} catch (error) {
reject(error);
}
});
const formData = await unstable_parseMultipartFormData(request, uploadHandler);
@eovandodev yes that is right, you need that as well, I have a project where I upload to supabase buckets as well and what I do is use the uploadHandler to upload it and return a url as the form value and then validate it as a string on the BE but what you're doing is right
will be closing this issue for now, file upload handling will be tacked as soon as the API in remix is stabilized
will be closing this issue for now, file upload handling will be tacked as soon as the API in remix is stabilized
@AlemTuzlak Thanks for the assistance!
Hi Thanks for your amazing work on this very useful library!
I've been trying to upload files too using the solution described above. I met an issue because I'm using the onValid
handler which is supposed to submit the form at the end:
const submit = useSubmit();
const form = useRemixForm<Output<typeof schema>>({
submitHandlers: {
onValid: (data) => {
// ...
const formData = createFormData(data);
submit(formData, { method: "post", encType: "multipart/form-data" });
},
},
}
The problem I met is that the createFormData
function returns a FormData object that contains a formData
, that contains a JSON-stringified version of the data. Then unstable_parseMultipartFormData
is not able to handle the data sent properly.
To fix it I had to turn the handler into this:
onValid: (data) => {
// ...
const formData = new FormData();
for (const key in data) {
formData.append(key, data[key]);
}
submit(formData, { method: "post", encType: "multipart/form-data" });
},
Does it make sense to you?
@oltodo @eovandodev the package now supports file uploads
Thanks @AlemTuzlak!
Do I still need to use unstable_parseMultipartFormData
to get the files?
So far, getValidatedFormData
returns an object where the file is a stringified version of the File object:
{
firstName: 'Bob',
lastName: 'Smith',
email: 'bob.smith@example.com',
bio: 'Nulla reprehenderit pariatur magna eu aliqua aliquip dolore mollit ullamco culpa exercitation aliquip exercitation id.',
picture: '[object File]'
}
Maybe could provide an example?
The following code worked for me:
const formData = await unstable_parseMultipartFormData(
request.clone(),
unstable_createFileUploadHandler({
directory: "/Users/nicolas/Code/AdventPrayers/public/assets/uploads",
}),
);
const {
errors,
data,
receivedValues: defaultValues,
} = await getValidatedFormData<Output<typeof schema>>(
request,
resolver(schema),
);
if (errors) {
return json({ errors, defaultValues });
}
const picture = formData.get("picture") as NodeOnDiskFile | null;
const user = await createUser({
...data,
picture: picture?.name || null,
});
@oltodo yes you have to use unstable_... Because of way how Remix works with files, but yes this is one of the ways you can do it, I'd recommend directly uploading it to a bucket if you're using one then returning the url from the handler
Great job with this awesome library! Unfortunately, I haven't been able to get it to work with a FileInput. I'm using Controller from react-hook-form and useRemixForm to handle the rest. The problem I'm facing is that whenever I submit the form, the value of the "avatar" field is always an empty object. Interestingly, on the client side, I've set up a watcher for the "avatar" field, and it is not empty. Do you have any ideas on what might be causing this issue?
Relevant code:
This is my squema