【问题标题】:Prevent component as array element from re-render when the parent array changes in React当 React 中的父数组发生变化时,防止组件作为数组元素重新渲染
【发布时间】:2020-07-19 05:26:47
【问题描述】:

我想创建一个简单的文件上传器,我已经有来自 dropzone 拖放的listOfFiles

文件的简单循环

{listOfFiles.map((file, i) => (
  <SingleFile
    key={i}
    index={i}
    file={file}
    handleDelete={handleDelete}
  />
))}

SingleFile 内部,当使用useEffect 进行组件挂载时,我会调用axios API 来上传文件,简化为:

useEffect(() => {
  if (!props.file) return;
  // ...
    // Axios call and bunch of states changes.
  // ...
}, [props.file]);

上传过程很好,当我拖动其他文件时它可以工作,新文件添加到listOfFiles,然后开始上传。

现在,当我点击SingleFile 内的删除按钮时,父组件上的handleDelete 会触发以删除具有特定索引的文件。

<button onClick={props.handleDelete(index)}> Delete </button>

handleDelete

setListOfFiles((listOfFiles) => listOfFiles.filter((f, i) => i !== index));

现在的问题是,当我删除 SingleFile 时,listOfFiles 数组被更新并且每个数组元素是 SingleFile 组件被重新渲染,并且再次调用上传 axios 调用......等等.

当另一个实例发生变化时,有没有办法不重新渲染组件的实例?让它在重新渲染时仅取决于它的状态/道具?并通过父数组中的唯一 id 或索引来控制它的挂载/卸载。

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    它可能会重新渲染,因为每个 SingleFile 组件上的 key 只是 listOfFiles 数组中相应项的索引。 React 使用key 来跟踪在呈现组件列表时哪个项目是哪个项目。所以如果你有:

    const listOfFiles = [
      'file0',
      'file1',
      'file2',
      'file3',
    ]
    

    你会有这些组件:

    <SingleFile key=0 file='file0' />
    <SingleFile key=1 file='file1' />
    <SingleFile key=2 file='file2' />
    <SingleFile key=3 file='file3' />
    

    键只是数组中的索引。因此,如果您删除假设第二个元素(即索引为 1 的元素,'file1'),那么 react 会认为它之后的所有元素都已更改(因为它们现在具有不同的数据)。你最终得到了这个数组:

    const listOfFiles = [
      'file0',
      // 'file1', // this element was removed
      'file2',
      'file3',
    ]
    

    还有这些成分:

    <SingleFile key=0 file='file0' />
    <SingleFile key=1 file='file2' />
    <SingleFile key=2 file='file3' />
    

    看看键 1 和键 2 现在有file2file3,以前有file1file2? React 现在认为键为 1 和 2 的组件发生了变化,将重新渲染。此外,由于file 已更改,您的useEffect 调用将再次运行。

    解决方案是将密钥设置为每个文件独有的内容,如果其中一个文件被删除,则不会更改的内容。文件名可能是:

    {listOfFiles.map((file, i) => (
      <SingleFile
        key={file.name}
        index={i}
        file={file}
        handleDelete={handleDelete}
      />
    ))}
    

    虽然多个文件可能具有相同的名称,但 React 不允许重复键。一个可能更好的解决方案是添加文件上传时间的时间戳(以微秒为单位)并将其包含在您的 file 对象中。仍然有重复的机会,但可能性要小得多:

    {listOfFiles.map((file, i) => (
      <SingleFile
        key={file.uploadedStamp}
        index={i}
        file={file}
        handleDelete={handleDelete}
      />
    ))}
    

    实际上,也许更好的解决方案是在上传时为每个文件添加一个随机值作为唯一 id。然后使用该唯一 ID 作为您的key

    {listOfFiles.map((file, i) => (
      <SingleFile
        key={file.id}
        index={i}
        file={file}
        handleDelete={handleDelete}
      />
    ))}
    

    您可能还想使用该 id 来自己识别文件,而不是使用 index

    {listOfFiles.map((file) => (
      <SingleFile
        key={file.id}
        id={file.id}
        file={file}
        handleDelete={handleDelete}
      />
    ))}
    

    然后你的按钮和handleDelete 看起来像这样:

    <button onClick={() => props.handleDelete(props.id)}> Delete </button>
    
    const handleDelete = (idToRemove) => {
      setListOfFiles(listOfFiles.filter((f) => f.id !== idToRemove));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-03
      • 2021-07-09
      • 1970-01-01
      • 2020-02-27
      • 2020-03-19
      • 2018-10-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多