3

I have a react hooks function that has a state object apiDATA. In this state I store an object of structure:

{
name : "MainData", description: "MainData description", id: 6, items: [
{key: "key-1", name : "Frontend-Test", description: "Only used for front end testing", values: ["awd","asd","xad","asdf", "awdr"]},
{key: "key-2", name : "name-2", description: "qleqle", values: ["bbb","aaa","sss","ccc"]},
...
]
}

My front end displays the main data form the object as the headers and then I map each item in items. For each of these items I need to display the valuesand make them editable. I attached a picture below.

enter image description here

Now as you can see I have a plus button that I use to add new values. I'm using a modal for that and when I call the function to update state it does it fine and re-renders properly. Now for each of the words in the valuesI have that chip with the delete button on their side. And the delete function for that button is as follows:

const deleteItemFromConfig = (word, item) => {
    const index = apiDATA.items.findIndex((x) => x.key === item.key);

    let newValues = item.value.filter((keyWord) => keyWord !== word);

    item.value = [...newValues];

    api.updateConfig(item).then((res) => {
      if (res.result.status === 200) {
        let apiDataItems = [...apiDATA.items];
        apiDataItems.splice(index, 1);
        apiDataItems.splice(index, 0, item);
        apiDATA.items = [...apiDataItems];
        setApiDATA(apiDATA);
      }
    });
  };

Unfortunately this function does not re-render when I update state. And it only re-renders when I update some other state. I know the code is a bit crappy but I tried a few things to make it re-render and I can't get around it. I know it has something to do with React not seeing this as a proper update so it doesn't re-render but I have no idea why.

2
  • FWIW, you only need a single .splice call: apiDataItems.splice(index, 1, item); Commented Apr 28, 2020 at 10:40
  • Yes I had that before but I just broke it down hoping it will do something :)). Thank you for the observation anyways. Commented Apr 28, 2020 at 10:41

2 Answers 2

8

It is not updating because you are changing the array items inside apiDATA, and React only re-render if the pointer to apiDATA changes. React does not compare all items inside the apiDATA.

You have to create a new apiDATA to make React updates.

Try this:

      if (res.result.status === 200) {
        let apiDataItems = [...apiDATA.items];
        apiDataItems.splice(index, 1);
        apiDataItems.splice(index, 0, item);
        setApiDATA(prevState => {
            return {
                ...prevState,
                items: apiDataItems
            }
       });
      }
Sign up to request clarification or add additional context in comments.

3 Comments

Your answer will be more useful if you explain what changed and why you changed it (i.e. why does it solve the problem).
" but the pointer to the array is the same" No, the array is different (let apiDataItems = [...apiDATA.items]; creates a new array). The problem is that apiDATA refers to the original value (and that's what you changed).
@FelixKling ahh yeah you are right, my mistake. I will correct the answer. Thanks
0

Using splice isn't a good idea, since it mutates the arrays in place and even if you create a copy via let apiDataItems = [...apiDATA.items];, it's still a shallow copy that has original reference to the nested values.

One of the options is to update your data with map:

const deleteItemFromConfig = (word, item) => {
  api.updateConfig(item).then((res) => {
    if (res.result.status === 200) {
      const items = apiDATA.items.map(it => {
        if (it.key === item.key) {
          return {
            ...item,
            values: item.value.filter((keyWord) => keyWord !== word)
          }
        }
        return item;
      })

      setApiDATA(apiData => ({...apiData, items});
    }
  });
}

1 Comment

You are not addressing the actual problem: Updating apiData itself (setApiDATA(data); is wrong).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.