如何处理阵列状态过滤器冲突



我目前遇到一个问题,使用数组筛选的多个setState相互干扰。基本上,如果用户上传了两个文件,并且它们大约同时完成,那么其中一个不完整的文件可能无法从数组中筛选出来。

我的最佳猜测是,之所以会发生这种情况,是因为他们分别过滤掉了需要过滤的那个,当第二个完成并从数组中过滤出来时,它仍然有第一个文件尚未过滤掉的旧的不完整数组的副本。解决这一问题的更好方法是什么?我是不是错过了一些显而易见的东西?我正在考虑使用一个对象来保存文件,但随后我需要为渲染部分创建一个自定义映射函数,以便它仍然可以像数组一样进行渲染。

fileHandler = (index, event) =>{
let incompleteFiles = this.state.incompleteFiles
incompleteFiles[index].loading = true
incompleteFiles[index].file = event.target.files[0]
this.setState({ incompleteFiles: incompleteFiles },()=>{
const fileData = new FormData()
fileData.append('file', event.targets[0].file)
let incompleteFiles = this.state.incompleteFiles
let completeFiles = this.state.completeFiles
api.uploadFile(fileData)
.then(res=>{
if(res.data.success){
this.setState(state=>{
let completeFile = {
name : res.data.file.name,
}
completeFiles.push(completeFile)
incompleteFiles = incompleteFiles.filter(inc=>inc.label !== res.data.file.name)
return{
completeFiles,
incompleteFiles
}
})
}
})
})
}

更新为已接受的答案,并对进行了细微调整

fileHandler = (index, event) =>{
this.setState(({ incompleteFiles }) => ({       
//  Update the state in an immutable way.       
incompleteFiles: [              
...incompleteFiles.slice(0, index),              
{                
...incompleteFiles[index],                
loading: true,                
file: event.target.files[0],              
},              
...incompleteFiles.slice(index+1)       
],     
}),  () => {
const fileData = new FormData()
fileData.append('file', event.targets[0].file)
api.uploadFile(fileData)
.then(res => {
if(res.data.success){
this.setState(({ incompleteFiles, completeFiles }) => ({
completeFiles: [
...completeFiles, // Again, avoiding the .push since it mutates the array.
{ // The new file.
name: res.data.file.name,
}
],
incompleteFiles: incompleteFiles.filter(inc=>inc.label !== res.data.file.name),
})))
}
})
});
}

在React中的类组件中,当设置从当前状态派生的状态时,您应该始终传递一个"状态更新器";函数,而不是只给它一个要更新的状态对象。

//  Bad
this.setState({ counter: this.state.counter + 1 });
// Good
this.setState((currentState) => ({ counter: currentState.counter + 1 }));

这样可以确保您获得状态的最新版本。需要这样做的事实是React如何在后台池状态更新的副作用(这使它更具性能(。

我认为,如果你重新编写代码以利用这种模式,它会是这样的:

fileHandler = (index, event) =>{
this.setState(({ incompleteFiles }) => ({
//  Update the state in an immutable way.
incompleteFiles: {
[index]: {
...incompleteFiles[index],
loading: true,
file: event.target.files[0],
},
},
}), () => {
const fileData = new FormData()
fileData.append('file', event.targets[0].file)
api.uploadFile(fileData)
.then(res => {
if(res.data.success){
this.setState(({ incompleteFiles, completeFiles }) => ({
completeFiles: [
...completeFiles, // Again, avoiding the .push since it mutates the array.
{ // The new file.
name: res.data.file.name,
}
],
incompleteFiles: incompleteFiles.filter(inc=>inc.label !== res.data.file.name),
})))
}
})
});
}

另一件需要记住的事情是避免更改状态对象。像Array.push这样的方法会使数组在适当的位置发生突变,这可能会导致问题和头痛。

我认为将代码更改为这样可以解决您的问题,并使代码易于阅读。

fileHandler = async (index, event) =>{
const incompleteFiles = [...this.state.incompleteFiles]
incompleteFiles[index].loading = true
incompleteFiles[index].file = event.target.files[0]
this.setState(
{
incompleteFiles
},
async (prev) => {
const fileData = new FormData()
fileData.append('file', event.targets[0].file)
const res = await api.uploadFile(fileData)
/// set loading state to false
incompleteFiles[index].loading = false

if (!res.data.success) {
return { ...prev, incompleteFiles }
}
// add new file name into completeFiles and remove uploaded file name from incompleteFiles
return {
...prev,
completeFiles: [...prev.completeFiles, { name : res.data.file.name }],
incompleteFiles: incompleteFiles.filter(inc=>inc.label !== res.data.file.name)
}
})
)
}

最新更新