title: React: Dynamic number of stateful items, AKA dealing with "Rendered more hooks than during the previous render"
body: <p>I'm trying to build a tree based forum similar to reddit and hackernews.  I'm using react and recoil to manage state.  I'm not sure if recoil is necessary for this particular problem as the state exists within a single page, but I'm open to using it in any potential solution.  I would prefer to stick with these libraries unless there is a convincing reason to branch out.</p>
  <p>I have a useEffect hook that fetches a thread's replies.</p>
  <pre><code>  useEffect(() =&gt; {
      async function fetchReplies() {
        if (!router.isReady) return;
          const res = await fetchWithTimeout(
            `${process.env.NEXT_PUBLIC_API_URL}/threads/${threadId}/replies`,
            {
              headers: {
                Authorization: `Bearer ${userToken}`,
              },
              timeout: 15000,
            }
          );
          //build replies object from response here
          setReplies(myRepliesObject);
      }
      fetchReplies();
    }, [router.isReady]);
  </code></pre>
  <p>These replies are then pumped into the thread body which loops over the data to create replies.</p>
  <pre><code>function ThreadBody(replies: any) {
    let replyItems = [];
    for (let i = 0; i &lt; replies.length; i++) {
      replyItems.push(ThreadReply(replies[i], 0));
    }
  </code></pre>
  <p>Like most tree based forums, I want to give users the ability to open and close particular reply trees.  To do so, I gave each reply a state of isOpen.</p>
  <pre><code>function ThreadReply(post, level) {
    const [isOpen, setIsOpen] = useState(true);
  ....
  </code></pre>
  <p>This worked great when I had a static json object to mock my replies.</p>
  <p>Trouble is, once I linked my backend and have a dynamic number of replies, I get the error <code>Rendered more hooks than during the previous render</code> upon setting replies.</p>
  <p>I've tried  <a href="https://stackoverflow.com/questions/64433884/how-to-get-all-elements-from-an-atomfamily-in-recoil">atomFamilies</a>, but I can't seem to figure out how I would set one (to update isOpen) with a dynamic atomFamilyId without violating the same constraint of more renders.</p>
  <p>It seems like it'd be possible for me to clone, edit, and replace the entire replies state, but that seems insanely expensive to set a single field in what could be a thousands long list.  I haven't gone down that road because I know I'll end up shipping it due to &quot;it works&quot; inertia.</p>
  <p>I'm self-taught when it comes to frontend and am surely missing some seminal knowledge.  Any recommendations?</p>