Home > database >  React - How to change number of array items mapped based on screen width?
React - How to change number of array items mapped based on screen width?

Time:10-13

I have an array that I am mapping over and rendering a component for each item. I want to reduce the number of items mapped from 5 to 4 at screen widths below 1194px, and to increase it back to 5 items above this width.

This is what I have tried so far but it causes the app to freeze if I mess around with the window size, and also initially renders 5 regardless of screen width.

This seems complex, is there an easier way to do this?

P.S - I have only included the relevant parts of the component.

// Section for loading specific number of cards

    // set number of anime to map via state so it can be change in media queries
    const [numberToMap, setNumberToMap] = useState(5);
    
    //dynamically load the anime cards so that they can use the 'number' variable
    const mapAnime = (number) => ( 
      <div className='anime-list-wrapper'>
        {animeData?.data?.slice(0, (number)).map((anime) => (
          <AnimeCard anime={anime} key={anime.id} />
        ))}
      </div> )

    window.matchMedia('(min-width: 1194px)').addEventListener('change', () => setNumberToMap(4));
    window.matchMedia('(max-width: 1195px)').addEventListener('change', () => setNumberToMap(5));   

  return (
    <div className='container'>
      <h3 className='category-title'>{categoryTitle}</h3>
      {!isFetching
        ? <div className="wrapper">
            {mapAnime(numberToMap)}
          </div>

CodePudding user response:

You're not cleaning up when the component unmounts. Basically you have a lot of event handlers for the change event whenever the components unmounts.

you should reverse addEventListener by using removeEventListerner("change", handler), but the requires to keep a reference to the handler.

Practically speaking, you need to set the event listeners in useEffect and return a clean up function.

const minHandler = () => setNumberToMap(4);
const maxHandler = () => () => setNumberToMap(5);
useEffect(() => {
  const minMedia = window.matchMedia("(min-width: 1194px)");
  minMedia.addEventListener("change", minHandler);

  const maxMedia = window.matchMedia("(max-width: 1195px)");
  maxMedia.addEventListener("change", maxHandler);
  return () => {
    minMedia.removeEventListener("change", minHandler);
    maxMedia.removeEventListener("change", maxHandler);
  };
}, []);

Or better still, you use a custom hook for matchMedia, which will take care of the cleanup.

CodePudding user response:

You're updating the react state on each screen width change. So when you were messing with it in the dev tools, you got freezes because of the sheer amount of state changes. The screen won't change often, but generally it is a good idea to avoid frequent unoptimized state updates. Anyways, your listeners are listening to the same thing, so one is unnecessary. Also, update the state only when events match.

Do this and you're good:

useEffect(() => {
        const mediaQuery = window.matchMedia('(max-width: 1194px)');
        const handler = (event: MediaQueryListEvent) => {
            if (event.matches) {
                setNumberToMap(4);
            } else {
                setNumberToMap(5);
            }
        };
        mediaQuery.addEventListener('change', handler);
        return () => mediaQuery.removeEventListener('change', handler);
    }, []);
  • Related