0

I wonder why useEffect doesn't invoke when a const array changed.
Can useEffect run only with array state changes ?

 const itemsInCart = [];

  useEffect(() => {
    if (itemsInCart === undefined)  
      setItemsQuantity('');

    } else if (itemsInCart.length > 99)  
      setItemsQuantity('99+');
     else {
      setItemsQuantity(itemsInCart.length)
    }
  }, [itemsInCart]);
skyboyer
  • 22,209
  • 7
  • 57
  • 64
ExtraSun
  • 528
  • 2
  • 11
  • 31
  • make the array into state. then it will trigger – Oliver Ilmjärv Aug 09 '21 at 10:46
  • @OliverIlmjärv What can I do if I don't want any re-renders when the array updates ? the `itemsInCart` array is being sent as props to many components, and when removing an item I don't want to re-render the page. – ExtraSun Aug 09 '21 at 10:48
  • you can also write a custom function, where you update the itemsInCart array that will also update the itemsQuantity value, you dont need to use useEffect – Oliver Ilmjärv Aug 09 '21 at 10:50
  • "What if I don't want any re-renders when the array updates ?" store array into `useRef` hook and access to it writing `itemsInCart.current` – Giovanni Esposito Aug 09 '21 at 10:50
  • @OliverIlmjärv the `itemsInCart`props is used in this component only in this `useEffect()`. – ExtraSun Aug 09 '21 at 10:52
  • @GiovanniEsposito I used your dvice and I'm getting an error on this `const array = [...itemsInCart];` `itemsInCart is not iterable` || so than when `const array = [...itemsInCart.current];` and this `array[i]['Quantity'] = 1;` the error is `Cannot add property Quantity, object is not extensible`. – ExtraSun Aug 09 '21 at 11:09

2 Answers2

0

useEffect works by checking if each value in the dependency array is the same as the one in the previous rendering and executes the callback if one of them is not. In your example you checking by the length so you don't need to pass an array itself in the dependencies just the length property will be fine, so your code will be like this:

 const itemsInCart = [];

  useEffect(() => {
    if (itemsInCart.length === 0)  
      setItemsQuantity('');
    else if (itemsInCart.length > 99)  
      setItemsQuantity('99+');
     else
      setItemsQuantity(itemsInCart.length)
  }, [itemsInCart.length]);

Note:

You can pass JSON.stringify(itemsInCart) as the dependency list if you want to track any changes in the array. Read more here

Ala Hamadi
  • 251
  • 2
  • 7
  • `itemsInCart.length` will make an error because it might be undefined – ExtraSun Aug 09 '21 at 12:16
  • Do the null check before accessing to the `length` like this `itemsInCart?.length`, [Optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) will help us in this regard. – Ala Hamadi Aug 09 '21 at 12:21
  • It passes it as if it was `false` - `(itemsInCart?.current.length === 0) ` to next else if condition – ExtraSun Aug 09 '21 at 12:39
  • Can you share your hole component ? Maybe there is something wrong with your code. – Ala Hamadi Aug 09 '21 at 13:42
  • I changed my code now `itemsInCart` array is defined as `useRef`, check my new question please - https://stackoverflow.com/questions/68712201/typeerror-cannot-add-property-quantity-object-is-not-extensible thanks – ExtraSun Aug 09 '21 at 13:52
0

If you want to keep the array in a state so that useEffect gets triggered, but at the same time you want to avoid causing re-renders when the array is changed, instead of using useState you should use useRef

const itemsInCart = useRef([]);
// do stuff
itemsInCart.current = newArray; // newArray holds the new value

This way when you change the value of itemsInCart the useEffect hook will be triggered, but the update operation itself will not cause re-renders.

Prinny
  • 208
  • 6
  • 16
  • Ok thanks - did I set the useReff() with localStroage correct here -https://stackoverflow.com/questions/68711409/useref-currrent-how-to-fix-cannot-read-property-current-of-undefined-and-m ? – ExtraSun Aug 09 '21 at 11:49
  • @ExtraSun did you create a new question based on my answer to your previous question? – Prinny Aug 09 '21 at 12:17
  • Based on @GiovanniEsposito comment. See my question was posted before your answer. – ExtraSun Aug 09 '21 at 12:18
  • 2
    @ExtraSun With full honesty and with no intention to be disrespectful, it seems there are a multitude of things wrong with your code that indicate a lack of understanding of React and javascript. For example you pass a `new Array()` to `JSON.parse()` which is wrong, another example is that you have a function that calls hooks (so, a hook) and then returns a value with no way to update said value (despite the useEffect you used). There is no easy way to answer your question, because there are several things going wrong at the same time. – Prinny Aug 09 '21 at 12:43
  • Isn't `new Array()` equals to `[]` ? because it works fine when I did it before it was useRef array, as- `const [itemsQuantity, setItemsQuantity] = useState(JSON.parse(localStorage.getItem(userItems)) || []);` – ExtraSun Aug 09 '21 at 13:46
  • @ExtraSun the `JSON.parse()` function expects a string and what you pass is DOMString || object. Try to run `JSON.parse(new Array())` in your console and you will see that it throws an uncaught syntax error. It may be that I am mistaken and you had the `|| []` inside the useState parentheses, if that was the case then my bad, but I stand by my original comment. – Prinny Aug 09 '21 at 13:52
  • My syntax mistake - it was supposed to be `useRef(JSON.parse(localStorage.getItem(userItems)) || []);` – ExtraSun Aug 09 '21 at 14:05