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> ); }
Advertisement
Answer
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])