I am learning React and have this simple problem I can't solve.
I created a Codesandbox
Here in the image: the file
is a key-value array like the image show.
After this code below has run:
return { ...file, md5: SHA256(fileContents).toString() };
Then the result is that key value pairs are removed like this image show:
As you see the file now only contains path
and md5
and everything else is gone.
I know this got to do with maybe shallow copy principle but I have search for a solution but can't understand how to fix this.
const FilePicker = ({ setNewFileForValidation }) => {
const readFileContents = async file => {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.onload = () => {
resolve(fileReader.result);
};
fileReader.onerror = reject;
fileReader.readAsBinaryString(file);
});
};
const readAllFiles = async AllFiles => {
const results = await Promise.all(
AllFiles.map(async file => {
const fileContents = await readFileContents(file);
return { ...file, md5: SHA256(fileContents).toString() };
}),
);
console.log(results, 'resutls');
return results;
};
function onDrop(acceptedFiles, rejectedFiles) {
readAllFiles(acceptedFiles).then(result => {
setNewFileForValidation(result);
});
}
return <Dropzone onDrop={onDrop} />;
};
The file
is ing from react-dropzone and contains the result from the file picker. Maybe that is the reason this spread copy does not work?
I am learning React and have this simple problem I can't solve.
I created a Codesandbox
Here in the image: the file
is a key-value array like the image show.
After this code below has run:
return { ...file, md5: SHA256(fileContents).toString() };
Then the result is that key value pairs are removed like this image show:
As you see the file now only contains path
and md5
and everything else is gone.
I know this got to do with maybe shallow copy principle but I have search for a solution but can't understand how to fix this.
const FilePicker = ({ setNewFileForValidation }) => {
const readFileContents = async file => {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.onload = () => {
resolve(fileReader.result);
};
fileReader.onerror = reject;
fileReader.readAsBinaryString(file);
});
};
const readAllFiles = async AllFiles => {
const results = await Promise.all(
AllFiles.map(async file => {
const fileContents = await readFileContents(file);
return { ...file, md5: SHA256(fileContents).toString() };
}),
);
console.log(results, 'resutls');
return results;
};
function onDrop(acceptedFiles, rejectedFiles) {
readAllFiles(acceptedFiles).then(result => {
setNewFileForValidation(result);
});
}
return <Dropzone onDrop={onDrop} />;
};
The file
is ing from react-dropzone and contains the result from the file picker. Maybe that is the reason this spread copy does not work?
-
1
Maybe File is some kind of a special object? Not every property of every object is enumerable. Could you avoid the problem by doing
return (file.md5=SHA256(fileContents).toString(),file);
? – loop Commented May 3, 2021 at 19:55 - 1 More on special properties: developer.mozilla/en-US/docs/Web/JavaScript/Reference/… – loop Commented May 3, 2021 at 19:58
-
@loop yes it works using
return (file.md5=SHA256(fileContents).toString(),file);
but then I mutate state I think, not sure – Kid Commented May 3, 2021 at 20:00 -
1
I'm not sure if
.map
can be used withasync/await
. – Andrii Lukianenko Commented May 3, 2021 at 20:01 - 4 Please provide data as text, not as a picture of text. Humans with visual impairments and search engine bots can't read your images. – Heretic Monkey Commented May 3, 2021 at 20:35
4 Answers
Reset to default 4File is a special object whose properties are not enumerable. Therefore the spread syntax is not working expectedly. You need to clone the File object using the File constructor.
readAllFiles = async (AllFiles) => {
const results = await Promise.all(
AllFiles.map(async (file) => {
const fileContents = await this.readFileContents(file);
// THIS IS THE PROBLEM
//return { ...file, md5: SHA256(fileContents).toString() };
file = new File([file], file.name, { type: file.type })
file.md5 = SHA256(fileContents).toString();
return file;
})
);
console.log(results, "result");
return results;
};
Maybe you can turn map to a for.
readAllFiles = async (AllFiles) => {
let results = [];
for (let file of AllFiles) {
const fileContents = await this.readFileContents(file);
results.push({ file, md5: SHA256(fileContents).toString() });
}
console.log(results, "result");
return results;
};
You can use Object.assign
instead of spread syntax so instead of:
{ ...file, md5: SHA256(fileContents).toString() };
use this:
Object.assign(file, { md5: SHA256(fileContents).toString() });
The accepted answer is correct in that object spread doesn't copy non-enumerable properties, but it isn't right about the fact that File
has non-enumerable properties. The reason the File's properties aren't copied when you spread is for a different reason.
Objects can have own properties as well as properties on their prototype. For example, the object {x: 1}
has x
as an own property, and then methods such as toString()
on its prototype. When you spread an object, only the own (enumerable) properties are copied to the newly created object. In the case of a File
object, it has no own properties (ie: it looks like {}
), and rather, all of its properties you see are maintained on the prototype ([[Prototype]]
) of file
(and on its prototype, etc.):
const file = new File(["foo"], "foo.txt", {
type: "text/plain",
});
console.log(Object.getOwnPropertyNames(file)); // [] (nothing here, so nothing is spread to the new object)
console.log(Object.getOwnPropertyNames(Object.getPrototypeOf(file))); // ["name", "lastModified", "lastModifiedDate", "webkitRelativePath", "constructor"] (not copied with `...` as they're on the prototype)
As a result, you can either create a new file object using new File()
and pass through the properties of your file
object to the constructor and then add your new property to this object (as shown in loop's answer), or, if you're fine with modifying the original file
object, you can do as other answers suggest and instead modify the file
object directly.