Search code examples
reactjsasync-awaitreact-hooksuse-effectuse-state

useEffect is not Triggering first time


 import React, { FC, useEffect } from "react"
 import YouTube from "components/YouTube"
 import { Typography } from "@mui/material"
 import { Container } from "layouts/Player/emotion"
 import Box from "@mui/material/Box"
 import VideoGrid from "components/VideoGrid"
 import { getPlayLists, getUser } from "utils"
 import { VideoCardProps } from "types/ComponentProps"
 import { getVideoById } from "api/youtube"
 import { IYouTubeVideo } from "types/YouTube"

 interface VideoValues {
     embedHTML: string
     title: string
     description: string
 }

 const Player: FC<{ videoId: string }> = ({ videoId }) => {
     const user = getUser()
     const [playlists, setPlaylists] = React.useState<VideoCardProps[]>([])
     const [video, setVideo] = React.useState<VideoValues>({
         embedHTML: "",
         title: "",
         description: "",
     })

     async function initialize() {
         const playlistTemp = await getPlayLists(user.playlist)
         const videoTemp: IYouTubeVideo = await getVideoById(videoId as string)
         setVideo({
             embedHTML: videoTemp.items[0].player.embedHtml,
             title: videoTemp.items[0].snippet.title,
             description: videoTemp.items[0].snippet.description,
         })
         setPlaylists(playlistTemp)
     }

     useEffect(() => {
         initialize()

         console.log("useEffect called")
     }, [])

     return (
         <Container>
             <Box>
                 <YouTube embedHtml={video?.embedHTML} title={video?.title} />
                 <Typography variant={"h5"} fontWeight={"600"}>
                     {video?.title}
                 </Typography>
                 <Typography variant={"body1"}>{video.description}</Typography>
             </Box>

             <Box>
                 <VideoGrid videos={playlists} />
             </Box>
         </Container>
     )
 }

 export default Player

In this code snippet, useEffect is skipped when the components are rendered, I don't know why. Can you please tell me what's the mistake I am doing here?

When I hardcode YouTube Component Props it works fine and useEffect is also executed. But the issue starts when I fetch video from API CALL

useEffect Not even Triggering first time


Solution

  • This is a regular old effect hook dependency problem.

    You will note that with standard linting, your effect hook will be complaining about missing dependencies...

    React Hook useEffect has a missing dependency: 'initialize'. Either include it or remove the dependency array.

    Were you to follow that advice though, you would see a new error

    The 'initialize' function makes the dependencies of useEffect Hook change on every render. Move it inside the useEffect callback. Alternatively, wrap the definition of 'initialize' in its own useCallback() Hook.

    If you then moved initialize into the effect hook, you would see

    React Hook useEffect has a missing dependency: 'videoId'.

    Which leads us to the root cause of all your problems... nothing is set to react to changes to the videoId prop.

    useEffect(() => {
      async function initialize() {
        const playlistTemp = await getPlayLists();
        const videoTemp: IYouTubeVideo = await getVideoById(videoId);
        setVideo({
          embedHTML: videoTemp.items[0].player.embedHtml,
          title: videoTemp.items[0].snippet.title,
          description: videoTemp.items[0].snippet.description,
        });
        setPlaylists(playlistTemp);
      }
    
      console.log("useEffect called");
    
      initialize();
    }, [videoId]);
    

    TL;DR never ignore warnings. They exist so you don't shoot yourself in the foot.