React Multiple services dependent causes rerendering

Viewed 17

I'm working on a react project. I wrote a script to get auth token to be passed to a custom hook in order to retrieve information. However, when I try to render this react component it would rerender itself to get the result. Here is the code:

const SpotifyLogin = () => {
  const [token, setToken] = useState(getToken().accessToken);
  const albums = useSpotifyData(token);
  console.log("Rendering ------");
  console.log("Has album:" + !!albums);
};

Here is the custom hook useSpotifyData(token)

export function useSpotifyData(token) {
  const [albums, setAlbums] = useState();

  useEffect(() => {
    SpotifyRequester("me/albums", token).then((data) => setAlbums(data));
  }, []);
  return albums;
}

The console would give me this on render:

Rendering ------
Has album: false
Rendering ------
Has album: true

How can I resolve this? What might be causing this rendering issue?

I've tried using logging token before passing into the custom hook. It has data before its been passed in. I have no clue why the rendering is triggered.

Any help is appreciated!

1 Answers

The logs seem completely fine/normal/expected to me.

In case you are misunderstanding the logs here's what is happening:

  1. albums state is initially undefined
  2. albums is undefined on the initial render cycle
  3. "Rendering ------" will log to console.
  4. "Has album:" + !!albums will log to console. Coercing albums to a boolean will log out false, i.e. "Has album: false".
  5. The useEffect hook in useSpotifyData runs and enqueues a state update. This triggers a rerender.
  6. albums is defined on the next render cycle.
  7. "Rendering ------" will log to console.
  8. "Has album:" + !!albums will log to console. Coercing albums to a boolean will now logout true, i.e. "Has album: true".

Code:

export function useSpotifyData(token) {
  const [albums, setAlbums] = useState(); // 1

  useEffect(() => {
    SpotifyRequester("me/albums", token)
      .then((data) => setAlbums(data)); // 5
  }, []);

  return albums;
}

...

const SpotifyLogin = () => {
  const [token, setToken] = useState(getToken().accessToken);
  const albums = useSpotifyData(token); // 2, 6

  console.log("Rendering ------"); // 3, 7
  console.log("Has album:" + !!albums); // 4, 8

  ...
};

Your question seems to be about getting this all down to a single render. The only way this would be possible is if you could synchronously set the album state when the hook is initialized/run on the initial render cycle. Since SpotifyRequester("me/albums", token) returns a Promise this isn't possible with the current code you've shared.

Related