I'm trying to make a dynamic form where the form input fields is rendered from data returned by an API.
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:
I made a small running example here https://codesandbox.io/s/zealous-night-e0h4jt?file=/src/App.tsx (same code as below):
import React, { useEffect, useState } from "react";
import { atom, RecoilRoot, useRecoilState } from "recoil";
import "./styles.css";
const textState = (key: string, defaultValue: string = "") =>
atom({
key,
default: defaultValue
});
const TextInput = ({ id, defaultValue }: any) => {
const [text, setText] = useRecoilState(textState(id, defaultValue));
const onChange = (event: any) => {
setText(event.target.value);
};
useEffect(() => {
return () => console.log("TextInput unmount");
}, []);
return (
<div>
<input type="text" value={text} onChange={onChange} />
<br />
Echo: {text}
</div>
);
};
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: "foo", type: "text", value: "bar" },
{ id: "hello", type: "text", value: "world" }
];
return (
<div className="App">
<RecoilRoot>
<form>
<button type="button" onClick={() => setTabIndex(0)}>
Tab 1
</button>
<button type="button" onClick={() => setTabIndex(1)}>
Tab 2
</button>
{tabIndex === 0 ? (
<div>
<h1>Fields</h1>
{fields.map((field) => {
if (field.type === "text") {
return (
<TextInput
key={field.id}
id={field.id}
defaultValue={field.value}
/>
);
}
})}
</div>
) : (
<div>
<h1>Tab 2</h1>Just checking if state is persisted when TextInput
is unmounted
</div>
)}
</form>
</RecoilRoot>
</div>
);
}
Is this even possible with recoil. I mean it seems to work but I can't ignore the warnings.
id
to the atomFamily though, if we can't even use it for anything? – Concentric