I have an input. On every change to the input, I want to call an API.
Here's a simplified version of the code:
// updated by input
const [urlText, setUrlText] = useState("");
const fetcher = useFetcher();
useEffect(() => {
if (urlText === "" || !fetcher) return;
fetcher.load(`/api/preview?url=${urlText}`);
}, [urlText]);
The issue is, when I put urlText inside of the dependencies array, there is an infinite rendering loop, and React claims the issue is I might be updating state inside of the useEffect. However, as far as I can tell, I'm not updating any state inside of the hook, so I'm not sure why an infinite re-render is happening.
Any thoughts?
The fuller version of the code is:
Note: The bug still happens without the debounce, or the useMemo, all of that stuff is roughly irrelevant.
export default function () {
const { code, higlightedCode } = useLoaderData<API>();
const [urlText, setUrlText] = useState("");
const url = useMemo(() => getURL(prefixWithHttps(urlText)), [urlText]);
const debouncedUrl = useDebounce(url, 250);
const fetcher = useFetcher();
useEffect(() => {
if (url === null || !fetcher) return;
fetcher.load(`/api/preview?url=${encodeURIComponent(url.toString())}`);
}, [debouncedUrl]);
return (
<input
type="text"
placeholder="Paste URL"
className={clsx(
"w-full rounded-sm bg-gray-800 text-white text-center placeholder:text-white"
//"placeholder:text-left text-left"
)}
value={urlText}
onChange={(e) => setUrlText(e.target.value)}
></input>
);
}
You might by setting state in useFetcher hook, please check code of load method from useFetcher.
The problem you're having is that fetcher is updated throughout the fetch process. This is causing your effect to re-run, and since you are calling load again, it is repeating the cycle.
You should be checking fetcher.state to see when to fetch.
useEffect(() => {
// check to see if you haven't fetched yet
// and we haven't received the data
if (fetcher.state === 'idle' && !fetcher.data) {
fetcher.load(url)
}
}, [url, fetcher.state, fetcher.data])
Update: I'm silly. useDebounce returns an array.