it's my first question. Please be forgiving if I have done something wrong.
Setup: I have a create-react-app which uses the react-router-dom for routing. The main idea is some kind of a simple file server. So the user can upload/drop a file on the webpage. This will be uploaded to a server and should then be listed on the page. For this the server provides an api endpoint which lists all files that were uploaded.
Because i want to use some kind of dropzone, which provides me the File object directly. I will not have a form in the end. So i think for this case i have to choose the useFetcher()
and then run fetcher.submit(...)
.
Problem: My problem is, that the file is not send to the server. Then I tried to find the source of it. I think it happens between the two lines with the comment (9 and 27). So the file wrapped inside a FormData gets some how stringified to only the name when passed to the other function.
What I've tried
As already mentioned, I checked in which step the problem occurs. So in my eyes it happens somewhere inside the library, which is "submitting" the formData to the action
function. So i looked inside the library, in the file dist/index.js
in the node module (react-router-dom: ^6.10.0) and in line 709 I found a function which can convert the formdata: getFormSubmissionInfo
. But i think because my input is of type FormData it will not be touched and returned in the same way as it was given to the function. (See line 161, dist/index.js
).
Question
Is useFetcher
the correct way to submit a file "in the background" to the server, so no form submit needed?
And if so, how can i provide the file to my action function?
Code snippet: To seperate the problem from my project I've created a github repo of my problem (https://github.com/fabalexsie/StackOverflow_SendFileWithReactRouterDomAction).
The loader and action functions are referenced in the routing object.
export async function loader() {
return fetch('http://localhost:8080/fileList').then(r => r.json())
}
export async function action({request}) {
const formData = await request.formData();
console.log("Formdata in action", formData) // here the file is only the name
await fetch('http://localhost:8080/upload', {
method: 'POST',
body: formData
});
return {success: true};
}
export function Details() {
const fileList = useLoaderData();
const fetcher = useFetcher();
const handleUpload = (ev) => {
const file = ev.target.files[0];
console.log("File from handle upload", file);
const formData = new FormData();
formData.append('file', file);
formData.append('pw', "InRealityComesFromSomeOtherInput");
console.log("Formdata in handle upload", formData); // here the file is the file object
fetcher.submit(formData, {method: 'POST', action: '/details'})
}
return (
<>
<ul>
{fileList.map(f =>
<li key={f}>
<a href={`http://localhost:8080/files/${f}`}>{f}</a>
</li>
)}
</ul>
<input type="file" id="file" name="file" onChange={handleUpload} />
</>
);
}
After a break, I looked into it again and found the error.
When submitting, the encoding type 'multipart/form-data' must be specified in order to send files as POST requests:
fetcher.submit(formData, {method: 'POST', action: '/details', encType: 'multipart/form-data'})
PS: I have also published the solution in the repo.