How I made a re-usable Table component with TypeScript and React

Thomas Allen - Aug 21 - - Dev Community

Trying to build on my very basic TypeScript knowledge, I decided to rebuild a project I built about a year ago using TypeScript. Trying to avoid the urge to put any everywhere, I've done some learning and made a start by building a component that was used thought my project. The Table.

In my previous project, I had about 3 separate Table components that were all slightly different and I just shipped it to get something out there. I knew it was horrible but I did it anyway.

This time, I wanted to build something reusable, type-safe and something I'd be happy showing off. So here it is:

import React from "react";

interface TableProps<T> {
  headers: string[];
  data: T[];
  renderRow: (item: T) => React.ReactNode;
}

const Table = <T,>({
  headers,
  data,
  renderRow,
}: TableProps<T>): JSX.Element => {
  return (
    <table>
      <thead>
        <tr>
          {headers.map((header) => (
            <td key={header}>{header}</td>
          ))}
        </tr>
      </thead>
      <tbody>
        {data.map((item, index) => (
          <tr key={index}>{renderRow(item)}</tr>
        ))}
      </tbody>
    </table>
  );
};

export default Table;
Enter fullscreen mode Exit fullscreen mode

Let's break it down. Firstly the interface. This describes the structure of the data used by the table. To make it reusable, the type of data can vary, therefore TypeScript generics need to be used. The data is an array of some type, T, the headers are just an array of strings, and I decided to create a renderRow function so I can pass in anything into the table rows which holds the shape of T.

TableProps<T> is like passing an argument into a function. We're saying if I want to use data: T[], I need to pass T into the interface. This will set the type of data to whatever the type is of the data we are passing in.

Similarly when defining the Table, we need to say that we are passing a generic type so we need to add <T,>. The props are in the shape of TableProps and return a JSX element:

({
  headers,
  data,
  renderRow,
}: TableProps<T>): JSX.Element => {...
Enter fullscreen mode Exit fullscreen mode

That's basically the TypeScript part of it. This is how it's used:

<Table headers={headers} data={data} renderRow={renderRow} />
Enter fullscreen mode Exit fullscreen mode

With renderRow defining the table rows, e.g.:

const renderRow = (user: User) => {
  return (
    <>
      <td>{user.name}</td>
      <td>{user.age}</td>
      <td>{user.address}</td>
      <td>{user.email.}</td>
    </>
  );
};
Enter fullscreen mode Exit fullscreen mode

This way of rendering rows maps over the data we pass it, and create a table row containing the item that is passed into the renderRow function.

Hope that all makes sense!

.
Terabox Video Player