Resolving redundancy in Typescript Material-UI Table codebases

Apologies for the ambiguous question title, it was difficult to come up with something more specific.

I am currently exploring the Typescript implementation of Material-UI tables, specifically focusing on the table section titled "Sorting and selecting". There is a helpful Sandbox demo available for reference.

As I work on multiple pages with tables, I noticed that by directly copying and pasting the code (with necessary data and style modifications), there is a significant amount of duplicated code. While I have been able to address most of these duplications, I am facing challenges with the EnhancedTableProps and EnhancedTableHead.

The issue lies in the fact that they rely on the Data interface, which differs for each table. This necessitates keeping the Data specific to each table and prevents unification.

I am struggling to find an appropriate solution mainly due to my limited experience with Typescript.

interface EnhancedTableProps {
  classes: ReturnType<typeof useStyles>;
  numSelected: number;
  onRequestSort: (event: React.MouseEvent<unknown>, property: keyof Data) => void;
  onSelectAllClick: (event: React.ChangeEvent<HTMLInputElement>) => void;
  order: Order;
  orderBy: string;
  rowCount: number;
}

function EnhancedTableHead(props: EnhancedTableProps) {
  const { classes, onSelectAllClick, order, orderBy, numSelected, rowCount, onRequestSort } = props;
  const createSortHandler = (property: keyof Data) => (event: React.MouseEvent<unknown>) => {
    onRequestSort(event, property);
  };

  return (
    <TableHead>
      <TableRow>
        <TableCell padding="checkbox">
          <Checkbox
            indeterminate={numSelected > 0 && numSelected < rowCount}
            checked={rowCount > 0 && numSelected === rowCount}
            onChange={onSelectAllClick}
            inputProps={{ 'aria-label': 'select all desserts' }}
          />
        </TableCell>
        {headCells.map((headCell) => (
          <TableCell
            key={headCell.id}
            align={headCell.numeric ? 'right' : 'left'}
            padding={headCell.disablePadding ? 'none' : 'normal'}
            sortDirection={orderBy === headCell.id ? order : false}
          >
            <TableSortLabel
              active={orderBy === headCell.id}
              direction={orderBy === headCell.id ? order : 'asc'}
              onClick={createSortHandler(headCell.id)}
            >
              {headCell.label}
              {orderBy === headCell.id ? (
                <span className={classes.visuallyHidden}>
                  {order === 'desc' ? 'sorted descending' : 'sorted ascending'}
                </span>
              ) : null}
            </TableSortLabel>
          </TableCell>
        ))}
      </TableRow>
    </TableHead>
  );
}

Answer №1

Implement a generic approach for Data

Explore how to create generic components in React using TypeScript here.

interface EnhancedTableProps<Data> {
  // ...
  onRequestSort: (event: React.MouseEvent<unknown>, property: keyof Data) => void;
}

function EnhancedTableHead<Data>(props: EnhancedTableProps<Data>) {
  // ...
}

Answer №2

That was the modification I needed to make.

interface HeaderCell<Data> {
  ...
}

interface CustomizedTableProps<Data> {
  ...
  headerCells: HeaderCell<Data>[];
}

function CustomizedTableHeader<Data>(props: CustomizedTableProps<Data>) {
  const { styles, onSelectionClick, sortOrder, sortedBy, totalSelected, totalRows, sortRequest, headerCells } = props;
}


            <CustomizedTableHeader
              ...
              headerCells={headerCells}
            />

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

obtain information beyond the prescribed boundaries

Having a slight issue, I am trying to return an array using a function in Angular. I am processing my data within an anonymous function and when I try to return the array, it ends up empty. I'm not sure what to do... getCurrentExchangeTo : function( ...

Unwillingness of Ajax to accept Names containing apostrophes as a parameter

$(".loadingPnl").removeClass('hdn'); var siteurlA = window.location.protocol + "//" + window.location.host + _spPageContextInfo.siteServerRelativeUrl; var callUrl = siteurlA + "/_layouts/15/SynchronyFinancial.Intranet/CreateMySite.aspx/S ...

Personalize headers in v-data-table while preserving default sorting capabilities

I'm looking to enhance my v-data-table by making the table headers "tab-able". To achieve this, I decided to create a slot and include tabindex on the columns. However, I encountered an issue where the sorting functionality stopped working. Does an ...

Serve the mobile version to mobile visitors and the desktop version to all other visitors

Can someone please explain to me how I can display the Mobile Version of a website when visiting on my phone, and the Desktop Version when viewing on anything larger than 500px? I have separately created both a Mobile Website and a Desktop Website. Is it ...

Having issues with Vue 3 Typescript integration in template section

This particular project has been developed using the create-vue tool and comes with built-in support for Typescript. Key versions include Vue: 3.3.4, Typescript: 5.0.4 Here is a snippet of the code to provide context: // ComponentA.vue <script setup l ...

React components need to refresh after fetching data from an API

I am currently working on a React application using TypeScript and integrating JSONPlaceholder for simulating API calls. I have successfully set up everything I need, but I am encountering an issue with re-rendering components that display response data fr ...

Error encountered at / - undefined local variable or method `parameters' for main:Object (Executing Stripe Charge with Stripe.js)

Encountering an error with the code below while attempting to create a Stripe charge using Stripe.js. Below is my web.rb file: require 'json' require 'sinatra' require 'sinatra/reloader' require 'stripe' get &a ...

"What exactly does {...props} refer to and what is the best way to obtain my function from another

Currently, I am focusing on learning react native with a specific interest in react navigation v5. As mentioned in this resource: https://reactnavigation.org/docs/themes/#using-the-current-theme-in-your-own-components, my goal is to implement a dark mode ...

Click on the navigation bar buttons to toggle between different divs and display multiple information boxes simultaneously

Here is the current code snippet... $("#contact").click(function() { if ( $( "#contact_div" ).length ) { $("#contact_div").remove(); } else { var html='<div id="contact_div" class="contact-info"><p>Contact info</p&g ...

Finding the Closest Element with jQuery's .closest() Method

I'm facing an issue while trying to select an element that is positioned above another element. Specifically, I want to target the nearest occurrence of the .discount-dropdown class that appears above the .discount-type class. Can anyone help me figur ...

Adding information to an array within a document in a MongoDB database

I am facing an issue where I am trying to add data to the readBook array in my User collection document. The code and console.log seem to indicate that the User is retrieved from the database and there is data to push, but nothing changes after the code ex ...

What is the reason for the Express middleware using parenthesis syntax, whereas custom-made middleware does not?

What is the reason behind Express inbuilt middleware using a parenthesis syntax like app.use(express.json()) while custom-made middleware does not use parentheses like app.use(logger)? It seems to throw an error with parentheses. I'm uncertain if th ...

jQuery menu animation not triggering until second click

I am currently working on implementing a menu that will slide in after clicking the hamburger button (located in the upper right corner). Instead of simply appearing, I wanted to use a jQuery function for a smoother sliding effect. However, I seem to be e ...

Employing various Class Methods based on the chosen target compiler option

Is there a way to instruct TypeScript to utilize different implementations of methods within the same class, based on the specified target option in the tsconfig.json file? I am currently transitioning one of my scripts to TypeScript to streamline managem ...

What is the best way to access and verify data from an array that has been passed through props

I am working with a component that takes an array as a prop <Component runners={['1','2']}/> Inside this functional component, my goal is to generate an element for each value in the array. Here's my logic: const { runners ...

Adjust dimensions of an image retrieved from a URL

Is there a way to adjust the size of the image displayed here?: var picture = new Image(); picture.src = 'http://www.example.com/images/logo.png'; picture.width = 200; //trying to change the width of the image $('canvas').css({ ...

Upgrading to React Router v6: Implementing Loader Functions with Context API

Having issues implementing loaders in React-Router V6 while making a request for a page through a function located in the context file. Unfortunately, I can't access the context from main.js where the router is defined. Main.js import ReactDOM from & ...

Achieving a transparent background in WebGLRender: A guide

I've been experimenting with placing objects in front of CSS3DObjects using the THREE.NoBlending hack. However, in the latest revisions (tested on r65 and r66), I only see the black plane without the CSS3DObject. Here is a small example I created: in ...

A pronounced distinction exists between ionInput and ionChange functionality

Q. Can you explain the difference between (ionInput) and (ionChange) events in Ionic framework? When would it be more appropriate to use one over the other? I have experimented with both event handlers below and found that they produce the same expected r ...

Tips for patiently anticipating the completed response within an interceptor

Using the interceptor, I want to display a loading spinner while waiting for subscriptions to complete. This approach works well in individual components by showing and hiding the spinner accordingly: ngOnInit() { this.spinnerService.show(); this. ...