首页 > 解决方案 > 在 React 中添加和删除 N 个文件输入

问题描述

我正在构建允许用户上传 N 个数据文件的东西。为此,我添加了一个按钮,该按钮将创建一个额外的文件上传选项。这是通过渲染函数中的一个简单的 for 循环完成的(有一个选择选项,只有在满足某些条件时才会出现,即“mergeColumnSelection”变量,你可以忽略它,但我将它包括在内,以防它以某种方式结束相关):

let renderedEnrichedDataFields = [];
for(let i = 0; i < this.state.enrichedData.length; i++) {
  let mergeColumnSelection = ""
  if(this.state.enrichedData[i] !== null) {
      mergeColumnSelection = <div className="form-item__select form-item">
        <label className="form-label" htmlFor="add-target-owner">Merging Column</label>
        <div className="form-item__container">
          <select onChange={(e) => {this.setEnrichedMergeColumn(e, i)}} defaultValue={this.state.csvColumns[0]}>
            {mainDataColumns}
          </select>
        </div>
      </div>
  }

  renderedEnrichedDataFields.push(
    <div className="form-group">
      <button onClick={() => {this.removeEnrichmentData(i)}} type="button" className="modal-close">
        <Icon name="close" />
      </button>
      <div className="form-item">
      <label className="form-label" htmlFor="add-target-csv">Enrichment Dataset</label>
        <input 
          className="csv-input" 
          type="file" 
          accept="text/csv" 
          onChange={(e) => {this.acceptNewEnrichedDataFile(e, i)}} 
        />
      </div>
      {mergeColumnSelection}

    </div>
  )
}

基本上,每次按下按钮时,都会将一个新元素推入处于状态的丰富数据数组中。这会导致应用程序呈现额外的文件输入。当用户上传文件时,数组中的占位符元素将替换为文件。当用户最终提交表单时,将提交一组文件,这很棒。

然而!我在获得一个干净的实现来移除这些输入字段的能力时遇到了很多麻烦。功能

removeEnrichmentData(index) {
  let enrichmentData = this.state.enrichedData
  let enrichedMergeColumns = this.state.enrichedMergeColumns;

  enrichmentData.splice(index, 1);
  enrichedMergeColumns.splice(index, 1)

  this.setState({enrichedData: enrichmentData, enrichedMergeColumns: enrichedMergeColumns});
}

如您所见,它获取所选输入的索引,然后将其从生成 for 循环的数组中拼接起来。从数组中拼接出合适的文件,文件输入的个数是正确的。但是,显示的文件名存在问题。图片将有助于:

这里可以看到一个示例,有人准备上传三个文件,health、cluster、starbucks

这里可以看到一个示例,有人准备上传三个文件,health、cluster、starbucks

现在我选择从列表中删除集群项目(项目 2)。 它从状态中的文件列表中删除,只留下健康和星巴克。 然而,for 循环只是运行了两次并删除了最后一项——这意味着看起来 health 和 cluster 是剩下的两个文件,尽管实际上它们是 health 和 starbucks

现在我选择从列表中删除集群项目(项目 2)。它从状态中的文件列表中删除,只留下健康和星巴克。然而,for 循环只是运行了两次并删除了最后一项——这意味着看起来 health 和 cluster 是剩下的两个文件,尽管实际上它们是 health 和 starbucks

我考虑过将 JSX 块本身移动到状态,以便我可以专门针对我想要删除的 JSX 输入元素 - 但这种方法的成功有限,并且读到不建议将 JSX 置于状态。React 并没有真正内置方法来轻松删除特定输入,而且我无法在文件输入中设置默认值,因此我无法轻松地将各个输入与其对应的状态联系起来。

感觉它应该是一个如此简单的问题,我很困惑。任何帮助表示赞赏!

标签: javascriptreactjs

解决方案


@Miloš Rašić 是正确的-您最初的问题是您可能正在使用数组索引作为输入的键。因此,如果您有 10 个编号为 0...9 的输入,并且您删除了索引为 5 的输入,那么您仍然使用键 0..8 渲染项目,并且 React 认为最后一个已被删除。

根据您关于使用 UUID 的评论,听起来您正在render()方法本身中生成唯一 ID。 不要那样做! 永远不要render(). 当你这样做时,你每次都在告诉 React “这个项目与我们上次渲染的不同,请在此处销毁现有项目并用新项目替换它”。

相反,当您将新条目添加到您的 state 时,您应该生成这些唯一 ID 。例如:

class FileInputList extends Component {
    state = { inputs : [] }

    addNewFileInput = () => {
        const inputID = uuid();
        const newInputs = this.state.inputs.concat({id : inputID});
        this.setState({inputs : newInputs});
    }

    render() {
        const {inputs} = this.state;

        const inputList = inputs.map(inputEntry) => {
            return <input type="file" key={inputEntry.id} />
        });

        return inputList;
    }
}

推荐阅读