skip to Main Content

I’ve an object like the following:

{
  "selectedProducts": [
    {
      "selectionId": 1722249608951,
      "productId": "8174367932666",
      "variants": [
        {
          "variantId": "45186237989114",
          "price": "17.99",
          "title": "Chicken",
          "quantity": 1
        },
        {
          "variantId": "45186238021882",
          "price": "18.99",
          "title": "Beef",
          "quantity": 1
        },
        {
          "variantId": "45186238054650",
          "price": "16.99",
          "title": "Mushroom",
          "quantity": 1
        }
      ],
      "productTitle": "Burger"
    },
    {
      "selectionId": 1722249804165,
      "productId": "8174367932666",
      "variants": [
        {
          "variantId": "45186237989114",
          "price": "17.99",
          "title": "Chicken",
          "quantity": 1
        },
        {
          "variantId": "45186238021882",
          "price": "18.99",
          "title": "Beef",
          "quantity": 1
        }
      ],
      "productTitle": "Burger (2)"
    }
  ]
}

Now I wanna trigger the useEffect when the variants count inside the selectedProducts is changed. So I initially tried doing the following:

useEffect(() => {
  // Some Operations

  setFormState((prevState) => ({
    ...prevState,
    ...productOptions,
  }));

}, [
  ...formState.selectedProducts.map((product) => product.variants.length)
]);

But this produces a error like: Warning: The final argument passed to useEffect changed size between renders. The order and size of this array must remain constant.


So the way-around I implemented was to take another state that will store the total number of variants and then pass selectedProducts as dependency of another useEffect which will then change the totalVariantsCount. Then pass the totalVariantsCount as the dependency of the main useEffect that I wanted to trigger in the place when the variants count changed.

const [totalVariantsCount, setTotalVariantsCount] = useState(0);

useEffect(() => {
    const variantsCount = formState.selectedProducts.reduce(( acc, product ) => acc + product.variants.length, 0);
    setTotalVariantsCount(variantsCount);

}, [formState.selectedProducts]);

To check it I implemented the following:

useEffect(() => {
  console.log(`totalVariantsCount changed: ${totalVariantsCount}`);

  // REMOVE: Putting the value in the formState just for test, remove it when releasing.  
  setFormState((prevState) => ({ ...prevState, totalVariantsCount }));

}, [totalVariantsCount]);

This works for now but acts strange sometimes like – when this useEffect is triggered after the totalVariantsCount is changed, it consoleLogs the correct value every-time but changes the totalVariantsCount in the main formState only when the selectedProducts count is changed not when the variants count is changed.


So I was wondering what is the proper way to trigger a useEffect on change of a deeply nested object.

Thanks!

2

Answers


  1. i think you need to use your whole array and have further checking inside the useeffect this answer can help:

    Login or Signup to reply.
  2. The problem here is not deeply nested object property but that you try to depend on a field that doesn’t exist and change dynamically

    So creating a state to manage it is good idea.

    Otherwise you can possibly explicitly change the whole array outside of the component. Like on each change outside send ...yourArray to the component so every time it changes, it will be a new pointer and then you’ll be able to use [yourArray] as a dependency for useEffect

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search