简体   繁体   中英

React .map not re-rendering

I'm building a sorting algorithm visualizer, and in my return, I'm creating divs to represent vertical bars, in animatedBubbleSort() I'm swapping values in the state array on a timeout, the array IS being sorted, but what I expected to happen was that the.map function would be re-rendered each time the state is changed with updateArray(). But the.map function does not re-fire at all.

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

}

It's because you're using the index of the elements in the array as the key. React uses key to decide which elements to rerender; because your keys are always in the same order, React won't update anything. Try:

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

See https://reactjs.org/docs/lists-and-keys.html#keys for more, specifically the following:

We don't recommend using indexes for keys if the order of items may change. This can negatively impact performance and may cause issues with component state. Check out Robin Pokorny's article for an in-depth explanation on the negative impacts of using an index as a key . If you choose not to assign an explicit key to list items then React will default to using indexes as keys.

The other answers are correct, too.

Try using a unique value in the key, but the main problem is that TempArray = array assigns both variables to the same reference. Because of this, when React tries to compare array with tempArray they would be the same value, and this won't trigger a re-render.

To effectively make a copy of an array, try tempArray = [...array] to avoid making unwanted changes in the original array.

TLDR: Try updateArray(tempArr.slice(0))

I struggled with the same problem for quiet some time and the answers did not solve my problem.

If you use modifiedState.slice(0) a duplicate of the prepended object is created, then use setState(modifiedState.slice(0)) or in your case updateArray(tempArr.slice(0)). This forces.map-Operation to rerender.

For anyone who comes across this and is already using an ID as their key, my issue was I was doing a double map, but rendering the second array. I had to change my key from the parent map id, to the rendered map id so React could detect a change.

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

This won't work in every situation but I managed to fix my similar issue by using a random number as the index.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM