title: Dynamic atom keys in Recoil
body: <p>I'm trying to make a dynamic form where the form input fields is rendered from data returned by an API.</p>
  <p>Since atom needs to have a unique key, I tried wrapping it inside a function, but every time I update the field value or the component re-mounts (try changing tabs), I get a warning saying:</p>
  <p><a href="https://i.stack.imgur.com/8dShg.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/8dShg.png" alt="&quot;&quot;" /></a></p>
  <p>I made a small running example here <a href="https://codesandbox.io/s/zealous-night-e0h4jt?file=/src/App.tsx" rel="nofollow noreferrer">https://codesandbox.io/s/zealous-night-e0h4jt?file=/src/App.tsx</a> (same code as below):</p>
  <pre><code>import React, { useEffect, useState } from &quot;react&quot;;
  import { atom, RecoilRoot, useRecoilState } from &quot;recoil&quot;;
  import &quot;./styles.css&quot;;
  
  const textState = (key: string, defaultValue: string = &quot;&quot;) =&gt;
    atom({
      key,
      default: defaultValue
    });
  
  const TextInput = ({ id, defaultValue }: any) =&gt; {
    const [text, setText] = useRecoilState(textState(id, defaultValue));
  
    const onChange = (event: any) =&gt; {
      setText(event.target.value);
    };
  
    useEffect(() =&gt; {
      return () =&gt; console.log(&quot;TextInput unmount&quot;);
    }, []);
  
    return (
      &lt;div&gt;
        &lt;input type=&quot;text&quot; value={text} onChange={onChange} /&gt;
        &lt;br /&gt;
        Echo: {text}
      &lt;/div&gt;
    );
  };
  
  export default function App() {
    const [tabIndex, setTabIndex] = useState(0);
  
    // This would normally be a fetch request made by graphql or inside useEffect
    const fields = [
      { id: &quot;foo&quot;, type: &quot;text&quot;, value: &quot;bar&quot; },
      { id: &quot;hello&quot;, type: &quot;text&quot;, value: &quot;world&quot; }
    ];
  
    return (
      &lt;div className=&quot;App&quot;&gt;
        &lt;RecoilRoot&gt;
          &lt;form&gt;
            &lt;button type=&quot;button&quot; onClick={() =&gt; setTabIndex(0)}&gt;
              Tab 1
            &lt;/button&gt;
            &lt;button type=&quot;button&quot; onClick={() =&gt; setTabIndex(1)}&gt;
              Tab 2
            &lt;/button&gt;
  
            {tabIndex === 0 ? (
              &lt;div&gt;
                &lt;h1&gt;Fields&lt;/h1&gt;
                {fields.map((field) =&gt; {
                  if (field.type === &quot;text&quot;) {
                    return (
                      &lt;TextInput
                        key={field.id}
                        id={field.id}
                        defaultValue={field.value}
                      /&gt;
                    );
                  }
                })}
              &lt;/div&gt;
            ) : (
              &lt;div&gt;
                &lt;h1&gt;Tab 2&lt;/h1&gt;Just checking if state is persisted when TextInput
                is unmounted
              &lt;/div&gt;
            )}
          &lt;/form&gt;
        &lt;/RecoilRoot&gt;
      &lt;/div&gt;
    );
  }
  </code></pre>
  <p>Is this even possible with recoil. I mean it seems to work but I can't ignore the warnings.</p>