简体   繁体   English

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

[英]setState is only executing once in Array.map function

I've got an Array.map function for looping through new uploaded files. 我有一个用于遍历新上传文件的Array.map函数。

Because a user can upload files several times, the files must be added together in one array. 由于用户可以多次上传文件,因此必须将文件一起添加到一个阵列中。

Therefore I'm using the following code 因此,我正在使用以下代码

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

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

The problem is that setState is only be executed on the last item. 问题在于setState仅在最后一项上执行。 So if a nuser selects 5 files, only the last one is getting into the state. 因此,如果nuser选择5个文件,则只有最后一个进入状态。

A possible solution is to take the exesting array, push the new files and do one setState outside of the map function but I would like to know why or how I can get multiple setStates into one map function. 一种可能的解决方案是获取扩展数组,推送新文件并在map函数之外执行一个setState,但是我想知道为什么或如何将多个setStates集成到一个map函数中。

This is working 这工作

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

Thats because React batches states, that means setState does not actually set the state but it adds the state transition onto a queue, that will then get executed somewhen. 那是因为React会批处理状态,这意味着setState实际上并没有设置状态,而是将状态转换添加到队列中,然后在某个时间执行。 So in your case the queue will look like this (pseudocode): 因此,在您的情况下,队列将如下所示(伪代码):

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

As you can see, the states don't depend on each other. 如您所见,状态互不依赖。 Therefore react will only take the last state, and you will only see the last file. 因此,react只会采用最后一个状态,并且您只会看到最后一个文件。 Instead you could make one state dependend on the previous one: 相反,您可以使一个状态依赖于前一个状态:

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

This causes the queue to look like: 这导致队列看起来像:

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

However actually you could add all files in one step: 但是实际上您可以一步添加所有文件:

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

TLDR: Don't set the state if it depends on a previous state. TLDR:如果状态取决于先前的状态,请不要设置状态。 this.state inside of setState is forbidden and will only work "by accident". setState内的this.state被禁止,并且只能“偶然”工作。

The problem with the first loop is that you are overriding the existing item with each iteration, and because setState is async it's not safe to do that way 第一个循环的问题是每次迭代都覆盖现有项目,并且由于setState是异步的,因此这样做是不安全的

why not do this instead? 为什么不这样做呢?

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

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

The 2nd option seems to be the right one. 第二个选项似乎是正确的。
The map function should be used if you want to return a new array. 如果要返回新数组,应使用map函数。
You can also use reduce : 您还可以使用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});
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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