0

I'm bit in confusion, this should be pretty obvious but I'm getting lost trying to figure everything out.

I'm creating a form with React, and the user can select a few images. My initial goal is to display the preview, but for a reason I can't understand, I can't map the array of results even though I can see with a console.log() that it's not empty

Here is how I create the array:

constructor(props) {
    super(props);
    this.state = {
        // Some other elements
        m_filename: [],
    };
}

handleMultiUpload() {

    let files = document.getElementById('multiple_files_upload');
    let files_result = [];

    for(let i = 0; i < files.files.length; i++) {
        let reader = new FileReader();
        reader.readAsDataURL(files.files[i]);
        reader.onloadend = function() {
            // console.log(reader.result);
            files_result.push({
                "id": i,
                "name": files.files[i].name,
                "img": reader.result
            });
        }
    }

    this.setState({
        m_file: files_result,
    });
}

render() {

    const { m_file } = this.state;

    // rest of my code
}

From there, when I console.log the m_file variable, it returns something looking like this:

[
  0:
    id: 0
    name: "file.jpg"
    img: "[base64 file rendered here]"
  1:
    id: 1
    name: "cat.jpg"
    img: "[base64 file rendered here]"
  ...etc
] 

I'm trying to loop through m_file by doing the following:

{
  m_file !== null && m_file.map(function(tile) {
      return (
          <li key={tile.id}>
              <img src={tile.img}/>
          </li>
      )
}

The problems are multiples:

  • m_file.length returns 0
  • As a consequence I can't manage to loop through it and display my images
  • Even though the console.log() make them show as an array, when I console.log(typeof m_file), it returns object. I guess because it's a state object, but... how can I deal with it, while I have no problem mapping through json elsewhere?

I have the feeling I'm ignoring something obvious.

Thank you in advance

Jaeger
  • 1,686
  • 1
  • 30
  • 50
  • 4
    `FileReader` is async operation, so when you setState, `file_result` is empty. Try to `setState` inside the `reader.onloadend` function. – Belmin Bedak Feb 20 '18 at 11:29

1 Answers1

1

From MDN:

The FileReader object lets web applications asynchronously read the contents of files (or raw data buffers) stored on the user's computer, using File or Blob objects to specify the file or data to read.

Since dealing with FileReader is asynchronous, when you setState, your variable file_result is still empty array.

One of possible solution, is call setState inside reader.onloadend function, just after pushing items to the array

let self = this;
reader.onloadend = function() {
   // console.log(reader.result);
   files_result.push({
      "id": i,
      "name": files.files[i].name,
      "img": reader.result
   });
   self.setState({
       m_file: files_result,
   });   
}

Note: this keyword would be binded to FileReader Object, so correct reference shall be kept in variable.

Here is the nice explanation of Async stuff in Javascript - https://stackoverflow.com/a/4560233/3918577

Belmin Bedak
  • 9,011
  • 2
  • 40
  • 44