Home > Software design >  Why am I getting Too many re-renders in my code and how to resolve it?
Why am I getting Too many re-renders in my code and how to resolve it?

Time:01-17

I'm learning ReactJS and Javascript, and I've occurred this problem, I don't know why it acts like that, but I think I know that my function is rendering too many times by setting the State for setEpisodeList over and over again I guess, how do I resolve this in my code?

The info state contains an episodes object that has an array type.

I have over 1000 episodes in 1 array, so I want to split that 1 big episodes array that contains over 1000 episodes into smaller chunks, each chunk will be an array that contains 10 episodes in it, that's why I'm using episodeListChunk.push(info.episodes.splice(0, 10)).

So it'll become

[ 
 [chunk 1 - 10 episodes], 
 [chunk 2 - 10 episodes], 
 [chunk 3 - 10 episodes], 
 [chunk 4 - 10 episodes], ... 
]

If I use console.log(episodeListChunk), I see it's working fine.

What I want

But if I'm setEpisodeList(episodeListChunk) then I get too many re-renders. Does that mean every time the while loop runs it'll setState every time? If that's so, which approach should I be doing to handle these situations?

Here's my code:

function MovieWatch({ instance }) {
    const [info, setInfo] = useState([])
    const [done, setDone] = useState(false)
    const [episodeList, setEpisodeList] = useState([])

    let { slug } = useParams()

    const getInfo = async () => {
        const { data } = await instance.get("/getInfo?slug="   slug)
        setInfo(data.data)
        setDone(true)
    }

    useEffect(() => {
        getInfo()
    }, [])

    const chunkEpisode = () => {
        const episodeListChunk = []
        while (info.episodes.length) {
            episodeListChunk.push(info.episodes.splice(0, 10))
        }
        setEpisodeList(episodeListChunk)
    }

    return (
        <>
            <div>Hello info page</div>
            {!done ? (
                <Loading loading={true} background="#000000" loaderColor="#FFFF00" />
            ) : (
                <>
                    {info ? chunkEpisode() : console.log("")}
                    <div>Done loading</div>
                </>
            )}
        </>
    )
}

CodePudding user response:

You are setting info when the component is rendered. Then you are checking if the info is set and running the chunkEpisodes function. {info ? chunkEpisode() : console.log("")}. Info will be set every time so you are constantly running the chunkEpisodes function. Instead of running this check you should put it in a useEffect hook with info as a dependency and if info is set then run the chunkEpisodes function. Like so:

useEffect(() => {
  if(info){
    chunkEpisode()
  }
}, [info])

CodePudding user response:

In React, every time any state changes the whole component gets re-rendered.

When this happens React will go through JSX and try to detect changes to any state and re-render them.

Since, you have invoked chunkEpisode() in JSX, it is causing state change i.e episodeList. Because of state change, the JSX is re-renderd, invoking chunkEpisode() again. This is happening infinitely.

A better approach would be to invoke chunkEpisode() using useEffect hook.

useEffect(() => {
    if (info) {
      chunkEpisode()
    }
}, [info])

And delete this line

{info ? chunkEpisode() : console.log("")}

CodePudding user response:

After getting the response and setting info call chunkEpisode() with the data,rather than calling it from the return.

const getInfo = async () => {
    const { data } = await instance.get("/getInfo?slug="   slug)
    setInfo(data.data)
    chunkEpisode(data.data)
    setDone(true)
}

CodePudding user response:

After reading some of the above answers, I've tried to instead of creating 1 big array at first, then I just splice it whenever the first time I receive that big array, I don't know if my explanation is correct or not...

From what @Rejo Mathew ways of using chunkEpisode(data.data), then I wonders what's the point of setting info state, so I removed it, and from @Steve K with @MWO explained, I tried to do this. It's working fine now.

But still, I still do not want to do it this way, seems odd to me than the first one, because the first one seems cool to me, weird why it's not working.

Instead of setInfo, then use that info to implement into the function, I just use that data I received from without setting it, then merge the chunkEpisode into getInfo function, then use useEffect to just only call it once I guess.

Here's the code after I do it:

function MovieWatch({ instance }) {
    const [done, setDone] = useState(false)
    const [episodeList, setEpisodeList] = useState([])

    let { slug } = useParams()

    const getInfo = async () => {
        const { data } = await instance.get("/getInfo?slug="   slug)

        const episodeListChunk = []
        while (data.data.episodes.length) {
            episodeListChunk.push(data.data.episodes.splice(0, 10))
        }
        setEpisodeList(episodeListChunk)
        setDone(true)
    }

    useEffect(() => {
        getInfo()
    }, [])

    return (
        <>
            <div>Hello info page</div>
            {!done ? (
                <Loading loading={true} background="#000000" loaderColor="#FFFF00" />
            ) : (
                <>
                    {console.log(episodeList)}
                    <div>Done loading</div>
                </>
            )}
        </>
    )
}

The result after I changed my code this way.

Worked

But please give me more advice to optimize this.

CodePudding user response:

I guess your problem lays in the info.episodes.splice(0, 10) . Splice mutates the info state and triggers consecutive re-renders.

If you do it like this you'll see the error is gone and episodeList state is updated.

So depends on what you intended with slice you can go from here and modify the code below:

function MovieWatch({ instance }) {
  const [info, setInfo] = useState([])
  const [done, setDone] = useState(false)
  const [episodeList, setEpisodeList] = useState([])

  let { slug } = useParams()

  const getInfo = async () => {
      const { data } = await instance.get("/getInfo?slug="   slug)
      setInfo(data.data)
      setDone(true)
  }

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

  const chunkEpisode = () => {
      const episodeListChunk = []
      // while (info.episodes.length) {
      //     episodeListChunk.push(info.episodes.splice(0, 10))
      // }
      info.episodes.forEach((e)=>{
        episodeListChunk.push(e)
      })
      console.log(episodeListChunk);
      setEpisodeList(episodeListChunk)
  }

  return (
      <>
          <div>Hello info page</div>
          {!done ? (
              <Loading loading={true} background="#000000" loaderColor="#FFFF00" />
          ) : (
              <>
                  {/* {info ? chunkEpisode() : console.log("")} */}
                  {episodeList.length ? episodeList : (chunkEpisode(), console.log(""))}
                  <div>Done loading</div>
              </>
          )}
      </>
  )
}
  •  Tags:  
  • Related