【问题标题】:How to handle array state filter clashes如何处理数组状态过滤器冲突
【发布时间】:2021-03-26 15:57:39
【问题描述】:

我目前遇到一个问题,即使用数组过滤的多个 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),
              })))
          }
        })
    });
  }

【问题讨论】:

    标签: arrays reactjs asynchronous filter state


    【解决方案1】:

    在 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 这样的方法会就地改变数组,这可能会导致问题和头痛。

    【讨论】:

    • 这很好用,我确实必须将用于更新数组中对象状态的部分稍微修改为javascript 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) ], })
    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-26
      • 2019-11-14
      • 1970-01-01
      • 2013-06-16
      • 1970-01-01
      • 2017-07-14
      • 2013-09-01
      • 2015-04-07
      相关资源
      最近更新 更多