Home > Blockchain >  In my React application, how can I make sure that my "useState" state is up-to-date before
In my React application, how can I make sure that my "useState" state is up-to-date before

Time:08-30

In my React application, I have an array of objects implemented using a "useState" hook. The idea is to update an existing object within this array if an object with a specific "id" already exists or to add a new object if it does not.

The problem that I am encountering is that my state is not up-to-date before I am modifying it (= updating and/or adding objects). Therefore, the check whether an object with a certain "id" already exists fails.

Here is a (very much simplified) example demonstrating the problem:

import React, { useEffect, useState } from 'react';

interface Dummy {
  id: string;
  value: number;
}

const TestComponent = () => {
  const [dummies, setDummies] = useState<Dummy[]>([]);

  const addDummy = (id: string, value: number) => {
    const dummyWithIdAlreadyUsed = dummies.find(
      (dummy: Dummy) => dummy.id === id
    );
    if (!dummyWithIdAlreadyUsed) {
      setDummies((dummies) => [...dummies, { id: id, value: value }]);
    }
  };

  const test = () => {
    addDummy('dummy1', 1);
    addDummy('dummy1', 2);
    addDummy('dummy2', 3);
    addDummy('dummy2', 4);
    addDummy('dummy3', 5);
    addDummy('dummy4', 6);
    addDummy('dummy4', 7);
  };

  useEffect(() => {
    console.log(dummies);
  }, [dummies]);

  return <button onClick={test}>Test!</button>;
};

export default TestComponent;

The desired output is:

0: Object { id: "dummy1", value: 1 }
1: Object { id: "dummy2", value: 3 }
2: Object { id: "dummy3", value: 5 }
3: Object { id: "dummy4", value: 6 }

The actual output is:

0: Object { id: "dummy1", value: 1 }
1: Object { id: "dummy1", value: 2 }
2: Object { id: "dummy2", value: 3 }
3: Object { id: "dummy2", value: 4 }
4: Object { id: "dummy3", value: 5 }
5: Object { id: "dummy4", value: 6 }
6: Object { id: "dummy4", value: 7 }

How can I make sure that my state is up-to-date before executing setDummies?

CodePudding user response:

State updates are asynchronous and queued/batched. What that means here specifically is that the dummies variable (the one declared at the component level anyway) won't have an updated state until after all 7 of these operations have completed. So that if condition is only ever checking the initial value of dummies, not the ongoing updates.

But you do have access to the ongoing updates within the callback here:

setDummies((dummies) => [...dummies, { id: id, value: value }]);

So you can expand that callback to perform the logic you're looking for:

const addDummy = (id: string, value: number) => {
  setDummies((dummies) => {
    const dummyWithIdAlreadyUsed = dummies.find(
      (dummy: Dummy) => dummy.id === id
    );

    if (!dummyWithIdAlreadyUsed) {
      return [...dummies, { id: id, value: value }];
    } else {
      return dummies;
    }
  });
};

In this case within the state setter callback itself you're determining if the most up-to-date version of state (as part of the queue of batched state updates) contains the value you're looking for. If it doesn't, return the new state with the new element added. If it does, return the current state unmodified.

CodePudding user response:

This is a problem of closure, you can fix it by using an arrow function in the setter, check this article : https://typeofnan.dev/why-you-cant-setstate-multiple-times-in-a-row/

Maybe you can refactor your code to avoid calling setters many times in a row by doing javascript operation before calling the setter

for exemple :

const test = (data: Dummy[]) => {

// to remove duplicate and keep the latest
const arr = data.reverse()

const latestData = arr.filter(
(item, index) => 
arr.indexOf(item) === index
);

setDummies([...dummies, ...latestDummiesData])
};

  • Related