简体   繁体   中英

Why is setState not working when using FileReader to read file content?

I'm making a file input to select multiple images that can be pre-visualized.

To handle multiple images I put them in an array of the component's state, and map the pre-visualized images.

But when I select the images from the input and set the state with this.setState({imgArray: newArray}) , this.state.array.map(image=><img src={image}/>) doesn't re-render the selected images.

Code:

export default class posts extends Component {
  state = {
    fotos: ["/iconos/img.svg"] // If there are not img selected, it renders one image icon
  }

  onUploadVarious = e => {
    let newArray = []
    Object.values(e.target.files).map(file => {
      let nuevo = new FileReader()
      nuevo.onload = event=> newArray.push(event.target.result)
      nuevo.readAsDataURL(file)}
    )
    this.setState({fotos: newArray}) // the state is set correctly
  }
}

Render:

<div className=" border rounded" style={{"height":"30%", "overflow":"auto"}}>
    {this.state.fotos.map(foto =>
      <img src={foto||"/iconos/img.svg"}
        id="arch-preview"/>)} // it doesn't re-render when fotos state is changed
</div>

// input
<div className="mt-auto">
    <input multiple="multiple" type="file" 
    onChange={this.onUploadVarious} 
    className="form-control-file" name="file" />
</div>

FileReader reads file content asynchronously .

Due to this asynchronous nature, state is being set ie this.setState({fotos: newArray}) before data urls are set in newArray ie newArray.push(event.target.result) .

And that's the reason your selected files aren't showing up .

To fix it, you can use create Promise which gets resolved after load event of each file. And use Promise.all which would be resolved after each Promise has resolved and then use setState :

readAsDataURL = (file) => {
  return new Promise((resolve, reject) => {
    const fr = new FileReader()
    fr.onerror = reject
    fr.onload = function () {
      resolve(fr.result)
    }
    fr.readAsDataURL(file)
  })
}

onUploadVarious = (e) => {
  Promise.all(Array.from(e.target.files).map(this.readAsDataURL))
    .then((urls) => {
      this.setState({ fotos: urls })
    })
    .catch((error) => {
      console.error(error)
    })
}

This has some good examples of Promises and their executing orders . Also check this about using FileReader with Promise .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM