Show list with limit and show more button

Viewed 743

I'm trying to create a list of items where there are always 3 displayed per row and when i click on show more button another 3 will be displayed. Unfortunately I'm having trouble doing it using map and slice as using this approach always render 3 of the same items in one row and doesn't account for if the number of items is not dividable by 3. Any suggestions are welcomed

Here is what I have so far (the Math.random as a key is just a placeholder)

  const renderRow = (
    documentData: {
      fileName: string;
      description: string;
      contentUrl: string;
    }[],
  ) => {
    return documentData.slice(0, limit).map((documentsRow, i) => {
      return (
        <Grid key={Math.random()} container spacing={1}>
          <Grid container item xs={12} spacing={3}>
            {renderRowItem(documentsRow)}
            {renderRowItem(documentsRow)}
            {renderRowItem(documentsRow)}
          </Grid>
        </Grid>
      );
    });
  };

Here is a working sample - https://codesandbox.io/s/react-hooks-typescript-list-with-load-more-button-fvbue

2 Answers
import React, { FC, useState } from "react";
import ReactDOM from "react-dom";
import {
  Grid,
  Paper,
  useMediaQuery,
  Container,
  Button
} from "@material-ui/core";
import KeyboardArrowDownIcon from "@material-ui/icons/KeyboardArrowDown";
import { useStyles } from "./useStyles";

import { DOCUMENTS_MOCK_DATA } from "./documentsMockData";

const LIMIT_MOBILE = 1;
const LIMIT_WEB = 3;

export const App: FC = () => {
  const isMobile = useMediaQuery("500");
  const inititalLimit = isMobile ? LIMIT_MOBILE : LIMIT_WEB;

  const [limit, setLimit] = useState(inititalLimit);

  const { button, buttonWrapper, root, paper } = useStyles();

  const showMoreDocuments = () => {
    setLimit(limit + 3);
  };

  const renderRowItem = (documentData: {
    fileName: string;
    description: string;
    contentUrl: string;
  }) => {
    return (
      <>
        <Grid item xs={12} sm={4}>
          <Paper className={paper}>{documentData.fileName}</Paper>
        </Grid>
      </>
    );
  };

  const renderRow = (
    documentData: {
      fileName: string;
      description: string;
      contentUrl: string;
    }[]
  ) => {
    return documentData.slice(0, limit).map((documentsRow, i) => {
      return (
        <Grid key={Math.random()} container spacing={1}>
          <Grid container item xs={12} spacing={3}>
            {renderRowItem(documentsRow)}
          </Grid>
        </Grid>
      );
    });
  };

  const renderDocuments = (
    documentData: {
      fileName: string;
      description: string;
      contentUrl: string;
    }[]
  ) => {
    return (
      <Grid container spacing={1}>
        {renderRow(documentData)}
        <Grid className={buttonWrapper} container item xs={12}>
          <Button
            className={button}
            endIcon={<KeyboardArrowDownIcon />}
            onClick={showMoreDocuments}
          >
            show more documents
          </Button>
        </Grid>
      </Grid>
    );
  };

  return (
    <Container className={root}>
      {renderDocuments(DOCUMENTS_MOCK_DATA)}
    </Container>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Related