Search code examples
javascriptreactjsreact-hookssetstate

React-hook setState does not work as expected


I have the following code:

  const classes = useStyles();
  const [data1, setData1] = useState([]);
  const [searchedString, setSearchString] = useState("");
  console.log(data1);

  const fetchDataHandler = async () => {
    setData1([]);

    axios
      .get(`http://localhost:5000/select?articul=${searchedString}`)
      .then((response) => {
        dataStruction(response.data);
      })
      .catch((err) => {
        console.log(err);
      });
  };

  const dataStruction = (data) => {
    data.map((element1) => {
      if (element1.secondaryArt.startsWith("30")) {
        return setData1([...data1, { ...element1, level: 1 }]);
      }
    });
  };
  const onChangeSearchText = (event) => {
    setSearchString(event.target.value);
  };

I want whenever I call fetchDataHandler to be able to set data1 to empty array. Now it is working as that results are sticking every time I call fetchDataHandler. How can I do it?


Solution

  • Problem:

    Your Asynchronous handler dataStruction closes over data1 before a new render is triggered at setData1([]); (at the top of your async function).

    This happens because React state updates are batched and asynchronous.

    Simple Solution:

    If you get rid of (delete the line) setData1([]); and change setData1([...data1, { ...element1, level: 1 }]); to setData1([{ ...element1, level: 1 }]); then you will get an array with a new element in it without preserving the "old" elements.

    Alternative Solution:

    You can also wrap your state updates into functions like so:

    Turn this: setState("foo")

    Into this: setState((state, props) => "foo")

    The second form (passing a function instead directly a state) ensures that the correct state is referenced. So in your case the second state update would reference the updated state.