为什么使用 FileReader 读取文件内容时 setState 不起作用?

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

我正在将一个文件输入 select 多个可以预先可视化的图像。

为了处理多个图像,我将它们放在组件状态的数组中,并映射预可视化图像。

但是当我 select 输入图像并使用 this.setState({imgArray: newArray}) 设置状态时,this.state.array.map(image=><img src={image}/>) 不会重新渲染 selected 图像。

代码:

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

渲染:

<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 异步读取文件内容

由于这种异步性质,状态正在设置,即 this.setState({fotos: newArray}) 数据 url 设置在 newArray 之前,即 newArray.push(event.target.result)

这就是您选择的文件没有显示的原因。

要修复它,您可以使用创建 Promise,它在每个文件的 load 事件后得到解决。并使用 Promise.all ,它将在每个 Promise 解决后解决,然后使用 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 关于使用 FileReaderPromise.