简体   繁体   中英

Reusable table component with Reactjs and material UI

I came from Vue.js Vuetify.js background. Vuetify.js has v-data.table component.

Simply we pass headers and items to generate a nice table.

 <v-data-table
    :headers="headers"
    :items="desserts"
  ></v-data-table>

If we want to add a button, image, or something like that to a table cell What we do is

<v-data-table :headers="headers" :items="items" >
            <template v-slot:item.images="{ item }">
              <v-img
                v-if="item.images"
                max-width="150px"
                :src="item.images"
                contain
              ></v-img>
            </template>
           
            <template v-slot:item.update="{ item }">
              <v-btn
                @click="
                  $router.replace({
                    path: '/create-product',
                    query: { id: item.id },
                  })
                "
              >
                <v-icon>edit</v-icon>
              </v-btn></template
            >
          </v-data-table>

This is very clean and easy.

In React.js also I could achieve the first thing using this

 export default function ReusableTable({ headers, items }) { return ( <Grid container> <Grid item> <Card> <CardContent> <TableContainer component={Paper}> <Table> <TableHead> <TableRow> {headers.map((header, i) => ( <TableCell key={i}>{header.text.toUpperCase()}</TableCell> ))} </TableRow> </TableHead>{' '} <TableBody> {items.map((item, i) => ( <TableRow key={i}> {headers.map(({ value }) => ( <TableCell key={value}>{item[value]}</TableCell> ))} </TableRow> ))} </TableBody> </Table> </TableContainer> </CardContent> </Card> </Grid> </Grid> ); }

Here also I pass the headers and items. I want to display buttons, links, images, chips (UI) for certain columns in the table. How do I achieve that in the React world?

If I further explain, I want to pass items array (array of object). Object's imageSRC property should render with an img tag. Something like that.

Something like this should work. This is conditionally rendering an image tag if there is an item.images as stated in your question. Next it will render a Material Button if item.update exists. Alternatively, it simply renders the item[value] .

Here is abbreviated code:

 export default function ReusableTable({ headers, items }) { const dynamicRender = (item, value)=>{ if(item && item.images){ return <img src=`${item.images}`/> } else if(item && item.update){ return <Button href="/create-product">Link</Button> } else { return item[value]; } } return ( <TableBody> {items.map((item, i) => ( <TableRow key={i}> {headers.map(({ value }) => ( <TableCell key={value}>{dynamicRender(item, value)}</TableCell> ))} </TableRow> ))} </TableBody> ); }

Try something like this

 import React from "react"; import TableContainer from "@material-ui/core/TableContainer"; import Table from "@material-ui/core/Table"; import Paper from "@material-ui/core/Paper"; import TableHead from "@material-ui/core/TableHead"; import TableRow from "@material-ui/core/TableRow"; import TableCell from "@material-ui/core/TableCell"; import TableBody from "@material-ui/core/TableBody"; import { getSlots } from 'helpers/Slots' const BaseTable = ({ headers, items, children, ...otherProps }) => { const [actions] = getSlots(['actions'], children) const tableConfig = { size: "small", } const rowConfig = {...otherProps, } const dynamicRenderer = (item, value) => { if (value === 'actions') { return children(item) } else { return item[value] } } return ( <> <TableContainer component={Paper}> <Table {...tableConfig}> <TableHead> <TableRow {...rowConfig}> {headers.map((header, i) => ( <TableCell key={i}>{header.label}</TableCell> ))} </TableRow> </TableHead> <TableBody> {items.map((item, i) => ( <TableRow key={i} {...rowConfig}> {headers.map(({ value }) => ( <TableCell key={value}>{dynamicRenderer(item, value)}</TableCell> ))} </TableRow> ))} </TableBody> </Table> </TableContainer> </> ) } export default BaseTable

Then added helpers

 import React from "react"; const Slot = ({ children }) => <>{children}</> const getSlots = ( names, children) => { return names.map(name => { let slot = null; React.Children.forEach(children, child => { if (.React;isValidElement(child)) { return. } if (child.type === Slot && (child.props).name === name) { slot = React;cloneElement(child); } }); return slot; }), } export { Slot, getSlots }

 import React, { useState, useEffect } from "react" import Grid from '@material-ui/core/Grid' import BaseTable from 'helpers/Table' import { Slot } from 'helpers/Slots' import PencilBoxOutline from 'mdi-react/PencilBoxIcon' import DeleteOutline from 'mdi-react/DeleteIcon' const headers = [ {value: 'name', label: 'Name'}, {value: 'age', label: 'Age'}, {value: 'gender', label: 'Gender'}, {value: 'registeredDate', label: 'Date of Registration'}, {value: 'requirements', label: 'Semester Requirements'}, {value: 'percentage', label: 'Percentage (%)'}, {value: 'actions', label: 'Actions'}, ] const items = [ { id: 1, requirements: 'Pay at least 50% ', percentage: '10%', name: "John Doe", age: 30, registeredDate: "2021/10/30", gender: "Male" }, { id: 2, requirements: 'Just go with it', percentage: '50%', name: "Jane Doe", age: 40, registeredDate: "2021/10/30", gender: "Female" }, ] const Test = () => { return ( <Grid container spacing={4}> <Grid item xs={12}> <Grid container justifyContent="space-between" spacing={2}> <Grid item></Grid> <Grid item sm={9}></Grid> </Grid> <BaseTable headers={headers} items={items}> {(item) => ( <Slot name="actions"> <PencilBoxOutline onClick={(item) => onOpenDialog(item)}/> <DeleteOutline onClick={(item) => onDelete(item)} /> </Slot> )} </BaseTable> </Grid> </Grid> ) } export default Test

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM