1

I have the following API Call:

const router = useRouter();
const { albumQuery } = router.query;
const [albums, setAlbums] = useState([]);

const fetchAlbumsHandler = useCallback(async () => {
    setIsLoading(true);
    setError(null);
    try {
      const url = `http://ws.audioscrobbler.com/2.0/?method=album.search&album=${albumQuery}&api_key=MY_API_KEY&format=json`;
      const res = await fetch(url);
      const data = await res.json();

      if (!res.ok) {
        throw new Error("Something went wrong!");
      }

      const jsonAlbums = data.map(
      // JSON business Logic
      );

      setAlbums(transformedAlbums);
    } catch (error) {
      setError(error.message);
    }
    setIsLoading(false);
  }, []);

With the corresponding useEffect function:

useEffect(() => {
      fetchAlbumsHandler();
  }, [fetchAlbumsHandler]);

However, the API call takes ${albumQuery} as undefined on the first render due to NextJS implementation details. Is there a way for me to access the variable on the first render?

3
  • 1
    No - as you say, it's undefined on the first render. But you can make your code wait until the second render before doing anything. A simple albumQuery && fetchAlbumsHandler(); would suffice, no? Commented Jul 31, 2022 at 22:12
  • 1
    Why is the query not available on the initial render? If it isn't then the code needs to wait for when it is valid to make the GET request by checking if the albumQuery query value is available. Commented Jul 31, 2022 at 22:14
  • 2
    In Next.js, statically optimized pages are hydrated without query parameters. You'll have to use router.isReady to check/wait for the parameter to be available after hydration. See useRouter/withRouter receive undefined on query in first render and nextjs.org/docs/advanced-features/…. Commented Jul 31, 2022 at 22:28

1 Answer 1

1

No, if the albumQuery isn't available on the initial render then the code should handle waiting for it to become available.

The existing code is assuming albumQuery is available on the initial render and attempts to close it over in the useCallback hook. After this the useEffect hook is called and since fetchAlbumsHandler is now a stable reference the useEffect hook won't be retriggered nor will fetchAlbumsHandler be re-memoized since the useCallback hook has an empty dependency array.

Minimally albumQuery appears to be a dependency for the useCallback hook and/or the useEffect hook. If fetchAlbumsHandler isn't passed as a prop to children there's no real benefit to memoizing it. I suggest moving it into the useEffect hook callback and using albumQuery as a dependency.

Example:

const router = useRouter();
const { albumQuery } = router.query;
const [albums, setAlbums] = useState([]);

useEffect(() => {
  const fetchAlbumsHandler = async () => {
    setIsLoading(true);
    setError(null);

    try {
      const url = `http://ws.audioscrobbler.com/2.0/?method=album.search&album=${albumQuery}&api_key=MY_API_KEY&format=json`;
      const res = await fetch(url);
      const data = await res.json();

      if (!res.ok) {
        throw new Error("Something went wrong!");
      }

      const jsonAlbums = data.map(
      // JSON business Logic
      );

      setAlbums(transformedAlbums);
    } catch (error) {
      setError(error.message);
    }
    setIsLoading(false);
  }

  fetchAlbumsHandler();
}, [albumQuery]);
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.