title: React: Prevent infinite Loop when calling context-functions in useEffect
body: <p>In my react app, I am rendering different instances of <code>&lt;Item&gt;</code> Components and I want them to register/unregister in a Context depending if they are currently mounted or not.</p>
  <p>I am doing this with two Contexts (<code>ItemContext</code> provides the registered items, <code>ItemContextSetters</code> provides the functions to register/unregister).</p>
  <pre><code>const ItemContext = React.createContext({});
  const ItemContextSetters = React.createContext({
    registerItem: (id, data) =&gt; undefined,
    unregisterItem: (id) =&gt; undefined
  });
  
  function ContextController(props) {
    const [items, setItems] = useState({});
  
    const unregisterItem = useCallback(
      (id) =&gt; {
        const itemsUpdate = { ...items };
        delete itemsUpdate[id];
        setItems(itemsUpdate);
      },
      [items]
    );
  
    const registerItem = useCallback(
      (id, data) =&gt; {
        if (items.hasOwnProperty(id) &amp;&amp; items[id] === data) {
          return;
        }
  
        const itemsUpdate = { ...items, [id]: data };
        setItems(itemsUpdate);
      },
      [items]
    );
  
    return (
      &lt;ItemContext.Provider value={{ items }}&gt;
        &lt;ItemContextSetters.Provider value={{ registerItem, unregisterItem }}&gt;
          {props.children}
        &lt;/ItemContextSetters.Provider&gt;
      &lt;/ItemContext.Provider&gt;
    );
  } 
  </code></pre>
  <p>The <code>&lt;Item&gt;</code> Components should register themselves when they are mounted or their <code>props.data</code> changes and unregister when they are unmounted. So I thought that could be done very cleanly with <code>useEffect</code>:</p>
  <pre><code>function Item(props) {
    const itemContextSetters = useContext(ItemContextSetters);
  
    useEffect(() =&gt; {
      itemContextSetters.registerItem(props.id, props.data);
  
      return () =&gt; {
        itemContextSetters.unregisterItem(props.id);
      };
    }, [itemContextSetters, props.id, props.data]);
  
    ...
  }
  </code></pre>
  <p>Full example see this <a href="https://codesandbox.io/s/friendly-bash-evs9b?file=/src/App.js:1300-1509" rel="noreferrer">codesandbox</a></p>
  <p>Now, the problem is that <strong>this gives me an infinite loop</strong> and I don't know how to do it better. The loop is happening like this (I believe):</p>
  <ul>
  <li>An <code>&lt;Item&gt;</code> calls <code>registerItem</code></li>
  <li>In the Context, <code>items</code> is changed and therefore <code>registerItem</code> is re-built (because it depends on <code>[items]</code></li>
  <li>This triggers a change in <code>&lt;Item&gt;</code> because <code>itemContextSetters</code> has changed and <code>useEffect</code> is executed again.</li>
  <li>Also the cleanup effect from the previous render is executed! (As stated <a href="https://reactjs.org/docs/hooks-effect.html" rel="noreferrer">here</a>: &quot;React also cleans up effects from the previous render before running the effects next time&quot;)</li>
  <li>This again changes <code>items</code> in the context</li>
  <li>And so on ...</li>
  </ul>
  <p>I really can't think of a clean solution that avoids this problem. Am I misusing any hook or the context api?  Can you help me with any general pattern on how write such a register/unregister Context that is called by Components in their <code>useEffect</code>-body and <code>useEffect</code>-cleanup?</p>
  <p>Things I'd prefer not to do:</p>
  <ul>
  <li>Getting rid of the context altogether. In my real App, the structure is more complicated and different components throughout the App need this information so I believe I want to stick to a context</li>
  <li>Avoiding the hard removal of the <code>&lt;Item&gt;</code> components from the dom (with <code>{renderFirstBlock &amp;&amp; </code>) and use something like a state <code>isHidden</code> instead. In my real App this is currently nothing I can change. My goal is to track <code>data</code> of all existing component instances.</li>
  </ul>
  <p>Thank you!</p>