Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React does not refresh after an update on the state (an array of objects)

I'm displaying cards with a map function on my state. The state looks like this:

[{ position: "0", state: "pending" }, 
{ position: "1", state: "pending" }, 
{ position: "2", state: "pending" }]

In a function, I'm changing the "state" from "pending" to "right", but it does not refresh anything on the page.

export const Card: React.FC<ICardOwnProps> = ({ }) => {
    const [rollingCards, setRollingCards] = useState<IRollingCard[]>([{ position: "0", state: "pending" }, { position: "1", state: "pending" }, { position: "2", state: "pending" }])

    const handleClick = () => {
        let newArray = rollingCards;
        newArray.splice(0, 1)
        setRollingCards(newArray);
    }

    useEffect(() => { console.log("check changes") })

    return (
        <StyledCard className="Card">
            {rollingCards.map((v, i, a) =>
                <Container key={i} className={"position" + v.position} >{v.state}</Container>
            )}
            <div className="card__container--button">
                <button onClick={handleClick}>Oui</button>
                <button>Non</button>
                {rollingCards[0].state}
           </div>
        </StyledCard>
    );
}

From now, nothings changes, but when I add a new state piece that I update at the same time as the rollingCards, it'll update. I added a counter, and with that, it updates the page.

(I also tried to splice the array to see if React would update, it did not)

Any idea why I've got this weird behavior ?

Thanks.

like image 270
Theo Fenique Avatar asked Feb 11 '20 00:02

Theo Fenique


2 Answers

This is because React does a referential equality comparison of props/state when making a decision to rerender.

newArray has the same reference as rollingCards due to which the functional component does not re render on setRollingCards

Change

setRollingCards(newArray)

to

setRollingCards([...newArray])

This creates a new array (new reference) with the elements from newArray

Working solution: https://codesandbox.io/s/admiring-wave-9kgqq

like image 157
varoons Avatar answered Dec 02 '22 23:12

varoons


Because react uses Object.is to determine if the state has changed.

In your code, newArray is the same reference as rollingCards, you can use Object.is(newArray, rollingCards) to check.

const handleClick = () => {
    let newArray = rollingCards;
    newArray.splice(0, 1)   // Object.is(newArray, rollingCards) is true
    setRollingCards(newArray);
}

So you can change it like this.

setRollingCards(rollingCards.filter((item, index) => index !== 0);

Not only this method, as long as it is a different reference

like image 37
Puckwang Avatar answered Dec 02 '22 23:12

Puckwang