Reputation: 731
I am trying to iterate over the selected files in input, i got a weird bug that happens when i switch the below statement
Object.keys(event.target.files).forEach(key => {
const currentFile = event.target.files[key]
}
to
for (let key in event.target.files) {
const currentFile = event.target.files[key]
}
I don't understand whats the difference between these two statements, the first one works but the second one doesn't (results in weird bug where an object inside the loop becomes null.
Here is the full code :
onChange= (event) => {
const selectedFilesObject = event.target.files
let selectedFilesCount = Object.keys(selectedFilesObject).length
if (selectedFilesCount > 0) {
this.setState({ loading: true })
this.props.onLockdownChange(true)
let readFilesResult = {}
for (let key in selectedFilesObject) {
const currentFile = selectedFilesObject[key]
readFileAsByteArray(currentFile)
.then(response => {
readFilesResult[key] = {
bytes: response,
name: currentFile.name,
type: currentFile.type,
}
})
.catch(() => {
readFilesResult[key] = null
})
.finally(() => {
selectedFilesCount = selectedFilesCount - 1
if (selectedFilesCount === 0) {
this.onReadingFilesFinished(readFilesResult)
}
})
}
}
}
export const readFileAsByteArray = (file) => {
return new Promise((resolve, reject) => {
var reader = new FileReader();
var fileByteArray = [];
reader.readAsArrayBuffer(file);
reader.onloadend = (evt) => {
if (evt.target.readyState == FileReader.DONE) {
var arrayBuffer = evt.target.result,
array = new Uint8Array(arrayBuffer);
for (var i = 0; i < array.length; i++) {
fileByteArray.push(array[i]);
}
resolve(fileByteArray)
}
}
reader.onerror = error => {
reject()
};
})
}
I just need to understand why using for loop causes readFilesResult
to have a null length! but using object.keys().forEach
doesn't!
Upvotes: 0
Views: 181
Reputation: 186
The difference between these two loops comes down to whether an object actually has a property, or inherited a property.
For example, if you have an object that inherited a property; Object.keys
will not return it, but a for in loop will. Which is why you need to call hasOwnProperty
method for each key you have in a for in loop.
In a more detailed way; for in loop is not bothered by the prototype chain.
// lets create a class called "Example".
class Example{
constructor(a,b) {
this.a = a;
this.b = b;
}
}
let instance = new Example(1,2);
for(let key in instance) {
console.log(key); // will only print "a" and "b". Which is to be expected.
}
// Let's add a member to all of the objects that are instantiated from the class Example using Example's prototype.
Example.prototype.newMember = "I exist";
// Let's run the for in loop again
for(let key in instance) {
console.log(key); // Prints "a", "b", and "newMember". The instances that are coming from "Example" doesn't have this... What happened there?
}
Turns out when you run a for-in loop, the loop doesn't recognize the prototype properties, and runs on all of them. Which is why I suggested running hasOwnProperty
check on your example. hasOwnProperty
checks to see if the member actually has a property, or is it a prototype property.
Does that make any sense?
Upvotes: 2
Reputation: 664385
You're enumerating a FileList
here, which as a DOM collection has item
and length
properties. You should use neither Object.keys
nor a for … in
loop here, rather a for (let i=0; i<files.length; i++)
iteration like on any array-like object. See also Why is using for…in
on arrays such a bad idea?.
Or just convert it into an array right away:
onChange = (event) => {
const selectedFiles = Array.from(event.target.files)
// ^^^^^^^^^^
if (selectedFiles.length > 0) {
this.setState({ loading: true })
this.props.onLockdownChange(true)
Promise.all(selectedFiles.map(currentFile =>
readFileAsByteArray(currentFile).then(response => ({
bytes: response,
name: currentFile.name,
type: currentFile.type,
}), err => null)
)).then(readFilesResult => {
this.onReadingFilesFinished(readFilesResult)
})
}
}
Upvotes: 3