setState在Array.map函数中仅执行一次

时间:2018-06-29 10:08:22

标签: javascript reactjs

我有一个Array.map函数,用于遍历新上传的文件。

由于用户可以多次上传文件,因此必须将文件一起添加到一个阵列中。

因此,我正在使用以下代码

onFileSelect = e => {
    //FileList to array
    const files = [...e.target.files];

    files.map(file => {
        this.setState({ invoices: [...this.state.invoices, file]})
    });
}

问题在于setState仅在最后一项上执行。因此,如果nuser选择5个文件,则只有最后一个进入状态。

一种可能的解决方案是获取扩展数组,推送新文件并在map函数之外执行一个setState,但是我想知道为什么或如何将多个setState集成到一个map函数中。

这正在工作

onFileSelect = e => {
    //FileList to array
    const files = [...e.target.files];

    const invoiceArr = this.state.invoices;

    files.map(file => {
        invoiceArr.push(file)
    });

    this.setState({invoices: invoiceArr});
}

3 个答案:

答案 0 :(得分:5)

那是因为React会批处理状态,这意味着setState实际上并没有设置状态,而是将状态转换添加到队列中,然后在某个时候执行。因此,在您的情况下,队列将如下所示(伪代码):

original -> original + files[0]
original -> original + files[1]
original -> original + files[2]
// flattened to:
original -> original + files[2]

如您所见,状态互不依赖。因此,react只会采用最后一个状态,并且您只会看到最后一个文件。相反,您可以使一个状态依赖于上一个状态:

files.forEach(file => {
    this.setState(previous => ({
        invoices: [...previous.invoices, file]
    }));
});

这会使队列看起来像:

original -> original + files[0]
previous -> previous + files[1]
previous -> previous + files[2]
// flattened to
original -> original + files[0] + files[1] + files[2]

实际上,您可以一步添加所有文件:

this.setState(previous => ({ invoices: previous.invoices.concat(files) }));
  

TLDR:如果它取决于先前的状态,请不要设置状态。 this.state内部的setState被禁止,只能“偶然”工作。

答案 1 :(得分:0)

第一个循环的问题是您每次迭代都覆盖现有项目,并且由于setState是异步的,因此这样做是不安全的

为什么不这样做呢?

onFileSelect = e => {
  //FileList to array
  const files = [...e.target.files];
  this.setState({ invoices: []})

  files.each(file => this.state.invoices.push(file)});
}

答案 2 :(得分:0)

第二个选项似乎是正确的。
如果要返回新数组,则应使用map函数。
您还可以使用reduce

onFileSelect = e => {

  const files = [...e.target.files];
  const invoiceArr = this.state.invoices;

  let invoicesUpdated = files.reduce((acc, file) => {
    acc.push(file);
    return acc;
  }, invoiceArr);

  this.setState({invoices: invoicesUpdated});
}