React中的.map方法不重新渲染

18
我正在构建一个排序算法可视化程序,在我的返回值中,我创建div来表示垂直条形图。在animatedBubbleSort()函数中,我使用setTimeout()函数交换状态数组中的值。虽然数组已经被排序,但我原本期望的是每次使用updateArray()更改状态时.map函数会重新渲染。但是.map函数根本没有重新触发。
import React, { useState } from "react";
import "../styles/App.css";
import { Header } from "./Header";

export default function SortingVisualizer(props) {
    const LOWER_BOUND = 5;
    const UPPER_BOUND = 200;
    const ARRAY_SIZE = 200;

const [array, updateArray] = useState(fillArrayWithRandomValues);

// returns a random number between bounds inclusive
function randomNumberBetweenBounds() {
    return Math.floor(Math.random() * UPPER_BOUND) + LOWER_BOUND;
}

// fills array with random values
function fillArrayWithRandomValues() {
    let tempArray = [];
    for (let i = 0; i < ARRAY_SIZE; i++) {
        tempArray.push(randomNumberBetweenBounds());
    }
    return tempArray;
}

function animatedBubbleSort() {
    let tempArr = array;
    let len = tempArr.length;
    for (let i = 0; i < len; i++) {
        for (let j = 0; j < len; j++) {
            if (tempArr[j] > tempArr[j + 1]) {
                let tmp = tempArr[j];
                tempArr[j] = tempArr[j + 1];
                tempArr[j + 1] = tmp;
                setTimeout(() => {
                    updateArray(tempArr);
                }, 300 * i);
            }
        }
    }
}

return (
    <div>
        <Header bubbleSort={animatedBubbleSort} />
        <div className="array-container">
            {array.map((value, idx) => {
                return (
                    <div
                        style={{ height: `${value * 2}px` }}
                        className="array-bar"
                        key={idx}
                    ></div>
                );
            })}
        </div>
    </div>
);

6
因为您使用索引作为键;React 使用key来确定重新渲染哪些元素,但是因为您的键始终以相同的顺序出现,所以React 不会更新任何内容。尝试使用“值”作为您的键。 - Hamms
1
请参见 https://reactjs.org/docs/lists-and-keys.html#keys 以获取更多相关内容。 - Hamms
6个回答

26

这是因为您将数组中的元素索引用作键。React使用key来决定重新渲染哪些元素;因为您的键始终以相同的顺序排列,React不会更新任何内容。尝试使用以下方法:

{array.map((value) => {
    return (
        <div
            style={{ height: `${value * 2}px` }}
            className="array-bar"
            key={value}
        ></div>
    );
})}
请参见https://reactjs.org/docs/lists-and-keys.html#keys以获取更多信息,具体如下:

如果项目的顺序可能会更改,则不建议使用索引作为键。这可能会对性能产生负面影响,并可能导致组件状态出现问题。查看Robin Pokorny的文章,了解使用索引作为键的负面影响的详细解释。如果您选择不将显式键分配给列表项,则React将默认使用索引作为键。


1
如果数组中存在相同的值怎么办? - mdehghani
1
@mdehghani 的想法是使其独特化。如果有重复的数组元素,则可以使用值+索引的组合,或者如果其他方法都失败了,只需加上时间戳即可 :) 每一刻都是独一无二的 - 包括现在。 - msanjay
3
天啊,非常感谢你!我已经找了很久了,有很多关于“为什么我的React组件不重新渲染”的问题,回答都说他们应该做我正在做的事情(即,不修复我的问题,因为我已经在那样做了),但是我最终通过将键值更改为帖子_id来解决了这个问题!我希望我能给你超过一个点赞。 - Raphael Morgan
兄弟,我已经为了解决这个问题烦恼了两个小时了。我非常感谢这条评论...谢谢你! - Alex Navarro

3
其他答案也是正确的。尝试在键中使用唯一值,但主要问题在于 TempArray = array 将两个变量分配给相同的引用。因此,当 React 尝试将 arraytempArray 进行比较时,它们将是相同的值,这不会触发重新渲染。为了有效地复制一个数组,请尝试 tempArray = [...array] 以避免对原始数组进行不必要的更改。

0
对于任何遇到这个问题并且已经使用 ID 作为键的人,我的问题是我进行了两次映射,但渲染第二个数组。我必须将我的键从父级映射 ID 更改为已呈现的映射 ID,以便 React 可以检测到更改。
results.map((product) => {
                  return product.variants.map((variant) => (
                    <div
                      key={variant.id} <-- changed from product.id to variant.id
                    >
                      <div>
                        {product.title} - {variant.title}
                      </div>
                      <div className='font-weight-light'>{variant.sku}</div>
                    </div>
                  ));
                })

0

这种方法并不适用于所有情况,但我通过使用随机数作为索引来解决了类似的问题。


0

简而言之:尝试使用updateArray(tempArr.slice(0))

我曾经为同样的问题苦苦挣扎,但是那些答案并没有解决我的问题。

如果你使用modifiedState.slice(0)创建了一个前置对象的副本,那么请使用setState(modifiedState.slice(0))或者在你的情况下使用updateArray(tempArr.slice(0))。这将强制.map操作重新渲染。


-2

只需要这样做 updateArray([...tempArr]),就能看到神奇的效果。


3
目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接