Search code examples
next.jsuse-effectnext-router

Prevent useEffect infinite loop when updating NextJs query string


I have a tag input that produces an array of tags. Using NextJs's useRouter, I want to add those tags as query string params as they are added. I also need to preserve the current query string params, since other filters, searches, and pagination need to remain.

Here is how I'm currently doing it.

const router = useRouter();
const { query } = router;
const [tags, setTags] = useState([]);

useEffect(() => {
  router.push({
    query: {
      ...query,
      tags,
    },
  });
}, [tags, router, query]);

return (
  <>
    <TagInput tags={tags} setTags={setTags} placeholder="Search by tags" />
  </>
);

However, this causes an infinite render since the useEffect updates the query but also has query as a dependency. If I remove query as a dependency, it works fine, but I get the missing dependency linting error.

Edit: Here is a codesandbox with a minimal example that reproduces the issue. It works as is, but if you uncomment the query dependency, infinite loop. https://codesandbox.io/s/next-js-dynamic-routing-forked-rlxuqh?file=/pages/index.js


Solution

  • One way this can be done is to directly update the query object and read the tags from the url, rather than storing the tags in state.

    const router = useRouter();
    const { query } = router;
    
    const setTags = useCallback((tags) => {
      router.push({
        query: {
          ...query,
          tags
        }
      });
    }, [router, query]);
    
    return (
      <>
        <TagInput tags={query.tags || []} setTags={setTags} placeholder="Search by tags" />
      </>
    );