'Why is the JavaScript spread notation not working here

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.

enter image description here

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:

enter image description here

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 coming from react-dropzone and contains the result from the file picker. Maybe that is the reason this spread copy does not work?



Solution 1:[1]

File 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;
  };

Solution 2:[2]

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;
};

Solution 3:[3]

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() });

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Serdar
Solution 3 Az.Youness