skip to Main Content

I’m trying to stream in data from an api, however during render, useEffect is called multiple times, in which during the initial page load, everything works just fine, aside from it being called multiple times, however when I refresh the page in the browser, the same thing happens, but teamData also gets set to null after it has loaded.

const router = useRouter();
    const { id } = router.query;

    const [teamData, setTeamData] = useState(null);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        GetTeamData(id).then((value) => {
            setTeamData(value);
            setLoading(false);
            console.log("Got Value")
        })
    }, [id])

The States are only referenced in the rest of the code, but never actually set again after this snippet. How would I get React/Next to stop resetting teamData to null, and as a bonus, how do I get it to only call useEffect once?

3

Answers


  1. The useEffect has the id as a dependency, so I’d guess that the value of the id changes more than one time and that causes this component to re-render. Although, I would suggest you to share more information, as this code snippet doesn’t show many things.

    Why don’t you use server-side props? Please see an example below.

    import type { InferGetServerSidePropsType, GetServerSideProps } from 'next'
     
    type Repo = {
      name: string
      stargazers_count: number
    }
     
    export const getServerSideProps: GetServerSideProps<{
      repo: Repo
    }> = async () => {
      const res = await fetch('https://api.github.com/repos/vercel/next.js')
      const repo = await res.json()
      return { props: { repo } }
    }
     
    export default function Page({
      repo,
    }: InferGetServerSidePropsType<typeof getServerSideProps>) {
      return repo.stargazers_count
    }
    
    Login or Signup to reply.
  2. When you doing useEffect with API calls you typically want to handle a couple of cases where your dependent values are null and where your component was unmounted:

    
    useEffect(()=>{
    
        if(!id) return; // do nothing if no id. 
        let wasUnmounted = false;
    
        fetch(...).then(resp=>resp.json()). // handle http success etc
            .then(data=>{
               if(!wasUnmounted) {
                   setState(data);
                   setTimeout(()=>setLoading(false), 0); // since setState would cause an unadorned setLoading to be ignored
               }
               return null;
            });
    
         return ()=>{
            wasUnmounted = true;
         };
    }, [id]);
    
    Login or Signup to reply.
  3. There are 2 reasons why your effect fires multiple times.

    1. Next.js pages router query parameters are undefined during the initial render – https://nextjs.org/docs/pages/api-reference/functions/use-router#router-object

    2. React 18 unmounts and remounts components in dev mode when using strict mode – https://react.dev/blog/2022/03/08/react-18-upgrade-guide#updates-to-strict-mode

    Please see if rewriting your effect as follows resolves your issues.

    useEffect(() => {
      let shouldCancel = false;
    
      if (id !== undefined) {
        GetTeamData(id).then((value) => {
          if (!shouldCancel) {
            setTeamData(value);
            setLoading(false);
            console.log("Got Value");
          }
        });
      }
    
      return () => {
        shouldCancel = true;
      };
    }, [id]);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search