-1

I have 3000+ tools data stored in my API, and I have used react-inifinite-scroll-component in order to show paginated view to the user, so that once they scroll so then only, they must see new data. I have a common useState called data1, where I concat the new data and I have used same state in order to render the cards. But what I can feel that once I render lot of cards and then again, I scroll back so I can see blank screen and after some time I see my cards back, ideally I don't want this behavior and need to provide smooth scrolling to the user, how can I do this. I have used React.memo() also in order to wrap my component. This is how I have implemented it.

<InfiniteScroll
      dataLength={data1.length}
      next={handleInfiniteScroll}
      hasMore={hasMore}
      loader={<div style={{
        display: 'flex',
        justifyContent: 'center',
        textAlign: 'center',
        marginBottom: '2rem',
        alignItems: 'center'
      }}><CircularProgress color="success" /></div>}
      endMessage={
        !loading && (
          (toolsCount === 0 && value) ?
            <NoResult subCategory={subCategory} />
            :
            (exclude) ? <div
              style={{
                display: 'flex',
                justifyContent: 'center',
                textAlign: 'center',
                marginBottom: '2rem',
                alignItems: 'center'
              }}
            >
              <Link href={`../category/${subCategory}`}>
                <a style={{
                  fontSize: '1.1rem',
                  color: '#8EA7E9',
                  fontWeight: 'bold',
                  textDecoration: 'underline'
                }}>
                  See All Alternatives
                </a>
              </Link>
            </div>
              :
              <AllSeen />

        )
        ||
        loading && (
          <div style={{
            display: 'flex',
            justifyContent: 'center',
            textAlign: 'center',
            marginBottom: '2rem',
            alignItems: 'center'
          }}><CircularProgress color="success" /></div>
        )
      }
      scrollThreshold={0.9}
    >
      <Grid container spacing={2} className={styles.productRoot}>
        


          {data1.slice(0, 3).map((item: any, index: number) => (
            <Grid key={item.slug} item xs={12} sm={6} md={4}>
              <div>


                <ProductCard
                  page={page}
                  item={item}
                  sort={sort}
                  favoriteCount={favoriteCount}
                  favorites={favorites}
                  setFavorites={setFavorites}
                  setFavoriteCount={setFavoriteCount}
                  data1={data1}
                  setData1={setData1}

                />
              </div>



            </Grid>
          ))}

        {
          (!loading && (page >= 1 && data1.length > 0)) && <div className={styles.newsletterCard}>
            <div className={styles.titleNewsText}>
              <Typography variant='h6' component="h6">Explore valuable AI tools that offer significant benefits.</Typography>
              <Typography variant='body1' component="h6">Get weekly updates on new AI tools by subscribing to our newsletter and joining our community. <a href="/newsletter">(Read Past Issues)</a></Typography>
            </div>
            <div className={styles.textFieldNews}>
              <TextField
                id="outlined-error-helper-text"
                placeholder="Email"
                type="email"
                value={email}
                size="medium"
                onChange={handleEmailChange}
                required
                error={!validEmail && email !== ""}
                helperText={(validEmail || email === "") ? '' : ''}
                style={{
                  background: '#142335',
                  borderRadius: '12px'
                }}
              />
              <Button
                endIcon={loading1 ? <CircularProgress size={20} /> : (
                  <ArrowForwardIcon style={{ fontSize: '1.2em' }} />
                )}
                onClick={() => {
                  (validEmail && email !== "") ? (loading1 ? enqueueSnackbar('Please Wait!', { variant: 'warning' }) : postEmail(email)) : enqueueSnackbar('Please Enter Correct Email ID!', { variant: 'error' })
                }}
              >Subscribe</Button>
            </div>
          </div>
        }



        {/* Render the remaining cards */}
 

          {data1.slice(3).map((item: any, index: number) => (
            <Grid key={item.slug} item xs={12} sm={6} md={4}>
              <div>

              
              <ProductCard
                page={page}
                item={item}
                sort={sort}
                favoriteCount={favoriteCount}
                favorites={favorites}
                setFavorites={setFavorites}
                setFavoriteCount={setFavoriteCount}
                data1={data1}
                setData1={setData1}
              />
              </div>
            </Grid>
          ))}

      </Grid>

    </InfiniteScroll>

I am stacking up the new data like this.

 setData1((prevData: any) => prevData.concat(newData)); 

This is the code of my ProductCard component

import React, { useState } from 'react'
import styles from "../Cards.module.scss"
import Image from 'next/image'
import Link from 'next/link'
import { Card, CardActions, CardContent, CardMedia, Typography, Button } from '@mui/material'
import FavoriteBorderOutlinedIcon from '@mui/icons-material/FavoriteBorderOutlined';
import FavoriteOutlinedIcon from '@mui/icons-material/FavoriteOutlined';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
import DoneIcon from '@mui/icons-material/Done';
import LockOpenIcon from '@mui/icons-material/LockOpen';
import LockIcon from '@mui/icons-material/Lock';
import SellIcon from '@mui/icons-material/Sell';
import PaidIcon from '@mui/icons-material/Paid';
import { makeFavourite, removeFavourite } from 'components/api/tool'
import { enqueueSnackbar } from 'notistack';
import { useSession, signIn, signOut } from "next-auth/react"




interface Props {
    page: any;
    item: any,
    sort: string | undefined,
    favoriteCount: any;
    favorites: any;
    setFavorites: any;
    setFavoriteCount: any;
    data1: any;
    setData1: any;
}

const ProductCard = ({ page, item, sort, favoriteCount, favorites, setFavorites, setFavoriteCount, data1, setData1 }: Props) => {
    const { data: session } = useSession<any>();



    // Compute the values for dynamic rendering.
    const title = item.verified_by_admin ? 'Featured' : item.name;
    const showBorder = item.verified_by_admin && sort === 'verified';
    const showPriceDiv = item.price_text && item.price_text !== '[]';
    const showFeatureDiv = item.verified_by_admin && sort === 'verified';

    const name = item.name;
    const slug = item.slug;
    const image_url = item.new_image_url;
    const price_text = item.price_text;

    const favorite_count = item.favorite_count;
    const short_description = item.short_description;
    const pricing = item.pricing;
    const subcategories = item.subcategories;
    const homepage_url = item.homepage_url;
    const id = item.id;
    const cacheKey = `${page}_response_${sort}`;


    let icon = null;
    switch (pricing[0]) {
        case 'Free':
            icon = <DoneIcon fontSize='small' />;
            break;
        case 'Freemium':
            icon = <LockOpenIcon fontSize='small' />;
            break;
        case 'Paid':
            icon = <PaidIcon fontSize='small' />;
            break;
        case 'Contact for Pricing':
            icon = <LockIcon fontSize='small' />;
            break;
        case 'Free Trial':
            icon = <LockOpenIcon fontSize='small' />;
            break;
        case 'Deals':
            icon = <SellIcon fontSize='small' />;
            break;
        default:
            icon = null;
    }

    const handleButtonClick = (item: any) => {
        if (!session) {
            enqueueSnackbar('Please login!', { variant: 'error' });
        } else {
            if (favorites[item.id]) {
                // Item is already favorited, so remove it from favorites
                // Update the API or perform necessary actions accordingly
                setFavorites((prevFavorites: any) => ({ ...prevFavorites, [item.id]: false }));
                setFavoriteCount((prevCount: any) => ({ ...prevCount, [item.id]: prevCount[item.id] - 1 }))
                removeFavourite([item.id])
                enqueueSnackbar('Tool unfavorited!', { variant: 'info' })
                // Update the session storage data for the item
                const newData1 = data1.map((dataItem: any) => {
                    if (dataItem.id === item.id) {
                        return { ...dataItem, is_favorite: false, favorite_count: dataItem[item.id] - 1 };
                    }
                    return dataItem;
                });
                setData1(newData1);
                sessionStorage.setItem(cacheKey, JSON.stringify(newData1));
            } else {
                // Item is not favorited, so add it to favorites
                // Update the API or perform necessary actions accordingly
                setFavorites((prevFavorites: any) => ({ ...prevFavorites, [item.id]: true }));
                setFavoriteCount((prevCount: any) => ({ ...prevCount, [item.id]: prevCount[item.id] + 1 }))
                makeFavourite([item.id])
                enqueueSnackbar('Tool favorited!', { variant: 'success' })
                // Update the session storage data for the item
                const newData1 = data1.map((dataItem: any) => {
                    if (dataItem.id === item.id) {
                        return { ...dataItem, is_favorite: true, favorite_count: dataItem[item.id] + 1 };
                    }
                    return dataItem;
                });
                setData1(newData1);
                sessionStorage.setItem(cacheKey, JSON.stringify(newData1));

            }
        }
    }



    return (
        <Card
            key={slug}
            title={title}
            sx={{ maxWidth: 310, margin: 'auto' }}
            style={showBorder ? { border: '2px solid #29b6f6' } : {}}
            className={styles.cardDivRoot}
            elevation={0}
        >
            {showPriceDiv && (
                <div className={styles.priceUpperDiv}>
                    $ {price_text}
                </div>
            )}

            {showFeatureDiv && (
                <div className={styles.featureUpperDiv}>
                    Featured
                </div>
            )}

            <Link href="/tools/[main]"
                as={`/tools/${slug}`}
            >
                {/* For the time being we have removed target blank for now */}
                <a target="_blank">


                    {/* <CardMedia
                  >
                    <div style={{ position: 'relative', width: '100%', height: '180px' }} >
                      <Image src={item.image_url} layout='fill' objectFit='cover' alt={item.name} placeholder="blur" blurDataURL='/blurImg.jpeg'/>
                    </div>
                  </CardMedia> */}

                    <CardMedia
                    >
                        <div style={{ position: 'relative', width: '100%', height: '180px' }} >
                            <Image src={image_url} layout='fill' objectFit='cover' alt={name} />
                        </div>
                    </CardMedia>
                    <CardContent className={styles.cardContent}>
                        <div className={styles.cardTitle}>
                            <Typography gutterBottom variant="h4" component="div">
                                {name}
                            </Typography>
                            <div className={styles.titleSaveDiv} title={`${item.favorite_count} favourited this tool`}>
                                <FavoriteBorderOutlinedIcon fontSize='small' />
                                {/* {item.favorite_count} */}
                                {favoriteCount[item.id]}
                            </div>
                        </div>
                        <Typography variant="body2" component="div" className={styles.cardShortDescription}>
                            {short_description}
                        </Typography>
                        <div className={styles.cardType}>
                            {icon}
                            {pricing[0]}
                        </div>
                        <div className={styles.cardHastags}>
                            {item.subcategories &&
                                item.subcategories.map((subCat: any) => (
                                    <Typography key={subCat} variant='body2' component="div">
                                        <div className={styles.circle}></div>
                                        {subCat}
                                    </Typography>
                                ))}
                        </div>
                    </CardContent>
                </a>

            </Link>

            <CardActions className={styles.cardBtnMain} >
                <Button size="small" title='Visit the website' onClick={() => {
                    window.open(item.homepage_url)
                }}><OpenInNewIcon /></Button>
                <Button size='small'
                    onClick={() => handleButtonClick(item)}
                    variant='outlined' title='Add to favourite'>
                    {favorites[item.id] ? (
                        <FavoriteOutlinedIcon className={styles.buttonIcon} />
                    ) : (
                        <FavoriteBorderOutlinedIcon className={styles.buttonIcon} />
                    )}
                </Button>
            </CardActions>
        </Card>
    )
}

export default React.memo(ProductCard)

Is there any way to optimize the performance. Here is the website: AI Toolhouse

Please help me out folks!

Need to optimize the performance on scrolling

0 Answers0