J.Tran
J.Tran

Reputation: 153

Reactjs: Unknown why function re-run second time

I started learning react, yesterday I ran into this issue, somebody please explain me. When I click button "add to wishlist" the piece of code below run 1st time, set product property "inWishList": true, but unknown why it rerun and set it back to "false" value.

 const AddToWishList = (id) => {
    setProducts((prev) => {
      const latest_Products = [...prev];
      const selected_Prod_Id = latest_Products.findIndex((x) => x.id === id);
      latest_Products[selected_Prod_Id].inWishList =
        !latest_Products[selected_Prod_Id].inWishList;

      console.log(latest_Products);
      return latest_Products;
    });
  };

_ The piece of code below works perfect, run only 1 time, however, i don't understand the difference between 2 codes

  const AddToWishList = (id) => {
    setProducts((currentProdList) => {
      const prodIndex = currentProdList.findIndex((p) => p.id === id);
      const newFavStatus = !currentProdList[prodIndex].inWishList;
      const updatedProducts = [...currentProdList];
      updatedProducts[prodIndex] = {
        ...currentProdList[prodIndex],
        inWishList: newFavStatus,
      };
      console.log(updatedProducts);
      return updatedProducts;
    });
  };

Upvotes: 1

Views: 191

Answers (1)

Drew Reese
Drew Reese

Reputation: 202846

In the first snippet you are mutating the state object when toggling the inWishList property.

const AddToWishList = (id) => {
  setProducts((prev) => {
    const latest_Products = [...prev];
    const selected_Prod_Id = latest_Products.findIndex((x) => x.id === id);

    latest_Products[selected_Prod_Id].inWishList =
      !latest_Products[selected_Prod_Id].inWishList; // <-- state mutation

    console.log(latest_Products);
    return latest_Products;
  });
};

The second snippet you not only shallow copy the previous state, but you also shallow copy the element you are updating the inWishList property of.

const AddToWishList = (id) => {
  setProducts((currentProdList) => {
    const prodIndex = currentProdList.findIndex((p) => p.id === id);
    const newFavStatus = !currentProdList[prodIndex].inWishList;

    const updatedProducts = [...currentProdList]; // <-- shallow copy state

    updatedProducts[prodIndex] = {
      ...currentProdList[prodIndex], // <-- shallow copy element
      inWishList: newFavStatus,
    };

    console.log(updatedProducts);
    return updatedProducts;
  });
};

Now the reason these two code snippets function differently is likely due to rendering your app into a StrictMode component.

StrictMode

Specifically in reference to detecting unexpected side effects.

Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:

  • Class component constructor, render, and shouldComponentUpdate methods
  • Class component static getDerivedStateFromProps method
  • Function component bodies
  • State updater functions (the first argument to setState)
  • Functions passed to useState, useMemo, or useReducer

When you pass a function to setProducts react actually invokes this twice. This is exposing the mutation in the first example while the second example basically runs the same update twice from the unmutated state, so the result is what you expect.

Upvotes: 2

Related Questions