简体   繁体   中英

Issue with React useState 2D array

I am facing strange behavior with useState. In the sandbox https://codesandbox.io/s/elastic-ellis-gjre7 I have 3 input fields, that change value if you click on them. The problem is with history state, that I log in console.

Expected behavior is, that as soon as you click on 1 of the boxes, it saves the state of all 3 boxes in history array, when we click another, it adds to history array new placements.

When clicking 1 box, it works correctly - "History 1" shows placement, and "History 2" and "History 3" are undefined.

But when we click 2nd box, it starts to act strange - It rewrites previously written "History 1", so now the content for "History 1" and "History 2" are the same. Strange, because all we do is concatenate new value to history array, but it overwrites the previous values.

Can anyone explain, why does it act in such a way?

You should write it as the following:

setHistory((prevHistory) => prevHistory.concat({ ...placements }));

Explanation: I believe what you are doing is you're merging the previous state with the current one, from what I understand you want to contact the arrays -correct me if wrong -

On line 16, newValue[event.value.id] is getting mutated. Meaning, the same value is being updated and referenced in each element of history .

To reach the expected behaviour, the solution is to reference different values. This can be achieved by way of cloning.

See this fork of your sandbox for a practical example.

// Mutation.
newValue[event.target.id].value = "X";

// Cloning.
const targetId = event.target.id
newValue[targetId] = {...newValue[targetId], value: "X"};

You have several things wrong.

  1. You are using item.id as event.target.id, but you are updating base on placements' array index, which you will eventually end up with unforseen results.

  2. You are updating history base on placements state, but when you are updating the history, the new placements might not be ready yet.

Here we will update your placements base on your item id instead of index. Then we update the history at the same time with the new placements.

  const handleClick = (event) => {
    // .map is commonly used in this type of situation to update the correct object in the array with new value.
    const newplacement = placements.map(item => item.id === parseInt(event.target.id) ? { ...item, value: 'X'} : item )
    
    setPlacements(newplacement);
    setHistory((prevHistory) => [...prevHistory, newplacement]);
  };

my sandbox

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