Wanda
Wanda

Reputation: 11

React - Optimize rendering array of JSX elements

I am using React 17.0.2 and I have a performance issue related to how I render an array containing a large number of JSX elements (it may vary from 100 to ~650 elements).

In particular, I would like to show, in a scrollable container, a certain number of different "Cards" where each one of them contains a chart, images, icons and paragraphs. These details change everytime the SSE sends new data.

The code below works just fine and does what I was expecting it to do but the rendering is quite slow. The app I am working on should be used on mobiles too and I am afraid that the waiting would be a tad too much for a user to bear.

Contestants:

const Contestants = memo(() => {
  return (
    <div className={`${'contestants-container animate__animated animate__fadeIn'} ${classes.mainContainer}`}>
      <div className={`${'mx-0 my-0'} ${classes.cardsContainer}`}>
        <PerfectScrollbar>
          <div className='d-flex flex-wrap align-items-center justify-content-evenly' >
            <DetailCards />
          </div>
        </PerfectScrollbar>
      </div>
    </div>    
  )
})
export default Contestants

DetailCards

const DetailCards = memo(() => {

 // ** Context
 const fetchDataContext = useContext(FetchDataContext)
 const vehicleData = fetchDataContext.vehicleData
 const locationData = fetchDataContext.locationData
 const payloadSSE = fetchDataContext.payloadSSE

 // ** State
 const [detailCardsArray, setDetailCardsArray] = useState([])
 const [initialLocationFlag, setInitialLocationFlag] = useState(true)
 const [initialLocationData, setInitialLocationData] = useState({})

 const detailCardsArrayRef = useRef(detailCardsArray)
 
 useEffect(() => {
   if (detailCardsArray) {
     detailCardsArrayRef.current = detailCardsArray
   }
 }, [detailCardsArray])

 useEffect(() => {
   setInitialLocationFlag(true)
 }, [])

 useEffect(() => {
   if (initialLocationFlag === true && Object.keys(locationData).length !== 0) {
     setInitialLocationData(locationData)
     setInitialLocationFlag(false)
   }
 }, [locationData, initialLocationFlag])

 useEffect(() => {
   if (Object.keys(vehicleData).length !== 0 && Object.keys(initialLocationData).length !== 0) {
     let temporaryDetailCardsArray = []
     for (let i = 0; i < Object.keys(vehicleData).length; i++) {
       temporaryDetailCardsArray.push(
         <DetailCard 
           key={vehicleData[Object.keys(vehicleData)[i]].imei} 
           object1={vehicleData[Object.keys(vehicleData)[i]]} 
           object2={initialLocationData[Object.keys(vehicleData)[i]]} 
         />
       )
     }
     setDetailCardsArray(temporaryDetailCardsArray)
   }
 }, [vehicleData, initialLocationData])

 useEffect(() => {
   if (detailCardsArrayRef.current.length !== 0) {
     if (typeof payloadSSE !== 'undefined' && payloadSSE !== null) {
       for (let i = 0; i < detailCardsArrayRef.current.length; i++) {
         if (detailCardsArrayRef.current[i].key === payloadSSE.imei) {
           detailCardsArrayRef.current[i] = (
             <DetailCard 
               key={detailCardsArrayRef.current[i].key} 
               object1={detailCardsArrayRef.current[i].props.object1} 
               object2={payloadSSE} 
             />
           )         
         }
       }
     }
     setDetailCardsArray(detailCardsArrayRef.current) 
   }
 }, [payloadSSE])
 
 return detailCardsArray
})

export default DetailCards

So, is there a way to render the array faster? I thought about virtualization but I am unsure about how to implement it.

Thank you!

Upvotes: 0

Views: 68

Answers (1)

Wanda
Wanda

Reputation: 11

The problem was solved by using @Piotr Żak suggestion to fetch a certain number of elements and give the user the option to load more. Furthermore, I added pagination as well. Now it works just fine even on mobiles.

Upvotes: 1

Related Questions