I will explain it here, as it took me a while to understand the explanations above, so I will try to make it simpler on others.
Answers for your questions:
1- Why is the return a function? return () => { ignore = true };
useEffect offers the use of return function, which is used for cleanup function purposes, OK!, When do you need a clean up? If u made a subscription for something, and u want to unsubscribe from it for example, you should add unsubscription logic in "return function" inside useEffect , instead of putting the logic in other places, that may cause a race condition!
2- What is ignored used for in this example?
The ignore is used as a flag that tells the function to ignore the api call or not. when is that used? when you have a race condition.
[example for a race condition - you can ignore this part of u are already familiar with race conditions] For example u click in list of products, and when each product is clicked you will update the page with this product info, if u click quickly in the products for many times , the useEffect will be updated whenever product_id is changed, and at some point clicking on product1 [will call api for product1 ]then quickly product2 [will call api for product2 and retrieve data causing -> setProduct(product2data) ] while still data for product1 has just arrived causing -> setProduct(product1data) . This way we have product2 clicked ! but product1 data appears at page!!.
BUTTTT WHAT THAT HAVE TO DO WITH ignore ??
[logic explanation]
ignore , my dear , tells the setProduct to ignore the old data, and not set it, this way we only will set the last clicked product, even if old data arrived later.
[code explanation]
1- first scenario : no races , product1 is clicked -> ignore is false , call api , retrieve data , if(!ignore ) -> will set product1 data , now clean up function will set ignore to true , congrats !.
2- second scenario -> race condition , product1 is clicked -> ignore is false, call api , [data still hasn't arrived] ,now product2 is clicked ignore is false , call api , data arrived , if(!ignore ) -> will set product2 data , clean up function will set ignore to true , product1 function data has arrived -> if(!ignore ) -> is false OOOPS, will not set product1 data "old data".
AND THIS WAY WE WILL ALWAYS HAVE NEW DATA . CHEERRRRRRRRS :D