Prevent Re-rendering inside a map React

927 Views Asked by At

I'm trying to make a clone of netflix, when I have the mouse over a image of the movies the trailer of that movie appears, there appears the problem when i mouseover i have many same outputs in the console. I think this problem is because im rendering inside a map, but im looking for avoid that for the performance... I tried with React.memo but doesn't work.

Here is an example

enter image description here

I have a parent component

function MovieList(props) {
...
    <Movies movies={netflixMovies} title="Movie Feature" />
}

Inside the child component i have the following

function Movie(props) {
...
const showMiniVideo = (id) => {
    setTimeout(() => {
      fetchYoutubeVideo(id) // Function who give me the ID of youtube video
        .then(res => {
          if (res.results.length === 0) { // If doesn't exist a video i set existVideo false
            setExistVideo(false)
            return setIdYoutubeVideo() // IdYoutubeVideo none
          }
          else {
            setExistVideo(true) // If exist a video i set existVideo true
            res.results.map(thriler => {
              return setIdYoutubeVideo(thriler.key) // IdYoutubeVideo id
            })
          }
        })
        .catch(err => {
          setExistVideo(false)
          console.error(err)
          return setIdYoutubeVideo()
        });
    }, 1000);
  }

...

return (
{movies.map(movie => (
              <div key={movie.id} className="tile">
                  <div onMouseOver={() => showMiniVideo(movie.id)} onMouseOut={stopMiniVideo}>
                    {idYoutubeVideo ?
                      (idMovie === movie.id ?
                        (existVideo ?
                          console.log("hay video")
                          :
                          console.log("no hay video")
                        )
                        :
                        console.log("no es el id")
                      )
                      :
                      <svg style={{ color: 'white' }} width="1.5vw" height="1.5vw" viewBox="0 0 16 16" className="bi bi-play-fill" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
                        <path d="M11.596 8.697l-6.363 3.692c-.54.313-1.233-.066-1.233-.697V4.308c0-.63.692-1.01 1.233-.696l6.363 3.692a.802.802 0 0 1 0 1.393z" />
                      </svg>
                    }
                  </div>
                </div>
              </div>
            ))}
)}

Any one have an idea? I would appreciate any help

3

There are 3 best solutions below

0
On

Every time you mouse over showMiniVideo() executed and fetching and setting state will cause re-render. try to maintain a flag when you playing on mouse over.

But I would say minimum number of console.log = length of array(movies) which is OK.

if more than that use a flag something like this -

if(isMouseOver) return;

return (
{movies.map(movie => (
              <div key={movie.id} className="tile">
                  <div onMouseOver={() => showMiniVideo(movie.id)} onMouseOut={stopMiniVideo}>
                    {idYoutubeVideo ?
                      (idMovie === movie.id ?
                        (existVideo ?
                          console.log("hay video")
                          :
                          console.log("no hay video")
                        )
                        :
                        console.log("no es el id")
                      )
                      :
                      <svg style={{ color: 'white' }} width="1.5vw" height="1.5vw" viewBox="0 0 16 16" className="bi bi-play-fill" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
                        <path d="M11.596 8.697l-6.363 3.692c-.54.313-1.233-.066-1.233-.697V4.308c0-.63.692-1.01 1.233-.696l6.363 3.692a.802.802 0 0 1 0 1.393z" />
                      </svg>
                    }
                  </div>
                </div>
              </div>
            ))}
)}

isMouseOver should be false only if all the states updated on execution of showMiniVideo() like - setExistVideo,setIdYoutubeVideo...

0
On

Ciao, yes you are right: with React onMouseOver does not have the same behaviour that has in normal HTML. In HTML will be triggered just one time. In React will be triggered for sure when you enter for the first time in object and then an uncertain number of times (sometimes only when you leave the object, sometimes more). Strange!

To solve this, you could use a debounce function that prevent this behaviour. Here an example that explain how to use debounce.

Anyway thanks, you pointed out something I didn't know!

0
On

I think the issue you have is not understanding the javascript listener mouseover itself. Mouseover get's called (pretty much) every time you move the mouse over the element you are listening on. What you probably want is mouseenter and mouseleave which both exist in react as onMouseEnter and onMouseLeave. The main difference here is that they both only fire once, once the pointer enters the area of the element, or respectively leaves it.

By the way, the MDN actually explains this behavior in the article about mouseover.

Feel free to get back to me in the comments, and happy coding =)