反应:列表项的奇怪调节结果,该列表项的内容由长度与列表相同的数组

问题描述

更新:问题尚未解决,但事实并非我第一次发布时的样子。我修改标题以更好地描述其真正含义。代码中的一些注释已更新,以节省一些阅读时间。 React确实应该重用元素,当两个数组的长度相同时,结果很奇怪。

Codesandbox用于快速检查。

我遇到了一个令人沮丧而又有趣的问题,并且已经苦苦挣扎了很长时间,没有运气。不知道这是否真的是React的错误

根据官方文档here,如果我理解此权利,则通过为元素提供正确的键(同级内唯一),React应该尝试更新和重用它,而不是销毁和重新创建它。重新渲染。

codepen上查看此示例,我已将其简化为足以显示问题的地方。原始代码也包含在下面,以供快速参考。

该应用程序即使简单也可以正常运行,因此增加/减少状态cur(通过单击两个按钮)来回滚动数字。

如果您在浏览器开发工具中进行了检查,以查看每次单击按钮后销毁并重新创建了哪些元素,那么很明显,每次单击+都会销毁第一个项目并创建最后一个项目,而每次单击-销毁最后一个项目并创建第一个项目,它们之间的其他<li> s仅被更新和重用。这是正确和预期的。

问题:

如果numloop的长度相同,例如将num更改为[3,4,5,6]删除任何两个项目都可以),因此它有4个项目与loop相同,我希望看到所有项目在单击按钮时都可以重复使用,并且没有该项目应该销毁并重新创建,因为所有项目都在同一个DOM树中,并且密钥易于设置。

但是,如果您再次签入浏览器开发工具,则它的工作原理确实有所不同,尽管渲染结果似乎没有问题。单击+会销毁第一个项目,并将其重新创建为最后一个项目,同时重用所有其他项目。单击-会重用第一项(最后一次渲染的最后一项),并销毁/重新创建所有其他项。

尽管该应用仍然可以正常运行(因为仅显示一些数字就很简单),但是所有类驱动的转换都会混乱,并且<li>下的所有子元素都将重新初始化,例如<img>重新加载源图像。

问题

  1. 在这种情况下,当两个数组的长度相同时,有什么特别之处?这里到底发生了什么?

  2. 可以或应该做些什么来解决它?意思是尝试重用现有项目。我想到的一种快速的肮脏方法是在num中插入一些虚拟项目,并在map()迭代中添加一些逻辑以跳过该虚拟项目。这行得通,但我不认为这是“正确”的方式。

import React,{ useState } from 'react';
import ReactDOM from 'react-dom';

const num = [1,2,3,6]; // array to serve content to list items
const loop = [1,4]; // array to be mapped to generate the list

const RollingNums = () => {
  const [cur,setCur] = React.useState(0); // control number rolling

  return (
    <div className="problem">
      <ul>
        {loop.map((value,index) => { // list items count is fixed
          // simple CS to get continued content from array num
          let idx = index + cur;
          idx =
            idx >= 0
              ? idx % num.length
              : ((idx % num.length) + num.length) % num.length;
          const target = num[idx].toString(); // content from array num for current element
          return (
            <li key={target} className={target}>
              {target}
            </li>
          );
        })}
      </ul>
      <div className="control">
        <button onClick={() => setCur((prev) => prev - 1)}>-</button>
        <span>{cur}</span>
        <button onClick={() => setCur((prev) => prev + 1)}>+</button>
      </div>
    </div>
  );
};

ReactDOM.render(<RollingNums />,document.querySelector('#root'));

解决方法

React似乎有问题,它确实以某种方式删除然后重新创建了这些项目。选中this,尤其是KunukNykjær提供的codesandbox

在我的工作中,我只是通过扩展第一个数组(num)以确保它比第二个数组(loop)长来快速进行了肮脏的绕行,在这种情况下,React对帐确实有效如预期的那样。就我的情况而言,这需要进行很多额外的检查,但这是我目前所能做的。