Executando verificação de segurança...
1

Componentes Genéricos no React (Table) ⚛️

💡 Este post é voltado para componentes tipados usando TypeScript


Uma das principais intenções na hora de criação de componentes em um projeto, é poder reutilizar um mesmo trecho de código em vários lugares, reaproveitando a lógica ou parte da interface. Para que o reaproveitamento seja mais fácil, é importante que o componente seja o mais genérico quanto possível, inclusive em suas tipagens.

Vamos a um exemplo prático de um componente genético no React, construindo um componente de <Table />.


Primeiro vamos falar um pouco sobre o exemplo.

O que vamos construir é parte de um site de petshop, que possui duas tabelas, a primeira exibe dados dos clientes e, a segunda exibe dados dos pets. Os tipos são os seguintes:

interface IClient {
  name: string;
  phone: string;
}

interface IPet {
  name: string;
  specie: 'dog' | 'cat' | 'bird';
}

Ficou claro que precisaremos de uma tabela que consiga exibir vários tipos de dados. Vamos fazer isso por inferir o tipo das props que foram passadas.

A base do nosso componente será essa:

interface IProps<T> {
  data: T[];
  columns: {
    heading: string;
    element: (row: T) => JSX.Element;
  }[];
}

export function Table<T>(props: IProps<T>) {
  // ...
}

Um scaffold bem comum para um componente React, mas com algo diferente, é possível ver que existe um tipo T sendo recebido na função e passado para a interface IProps, aonde esse tipo será inferido pelo TypeScript.

Então ao usarmos:

const [pets, setPets] = useState<IPet[]>([]);
const [clients, setClients] = useState<IClient[]>([]);

// ...

 <Table
    data={pets}
    columns={[
      {
        heading: 'Name',
        element: (row) => <p>{row.name}</p>,
      },
      {
        heading: 'Specie',
        element: (row) => <p>{row.specie}</p>,
      },
    ]}
  />

O TypeScript entende que estamos lidando com um array de IPet e por isso usamos as chaves name e specie sem erros (inclusive recebemos intelisense sobre elas). Porém, se alterarmos o valor do atributo data para o array de clientes, o TypeScript vai nos alertar de que a propriedade "specie" não existe no tipo IClient.

O que acontece por debaixo dos panos é que inicialmente o TypeScript não sabe o que T significa, mas quando passamos o array pets para a propriedade data, ele associa data = pets, logo T = IPet. Então nossa tabela consegue inferir o tipo de qualquer dado que for passado para ela!


Nosso componente de tabela completo seria algo do tipo:

import React from 'react';

interface IProps<T> {
  data: T[];
  columns: {
    heading: string;
    element: (row: T) => JSX.Element;
  }[];
}

export function Table<T>({ data, columns }: IProps<T>) {
  return (
    <table>
      <thead>
        <tr>
          {columns.map(({ heading }, index) => (
            <th key={`column-${index}`}>
              <p>{heading}</p>
            </th>
          ))}
        </tr>
      </thead>

      <tbody>
        {data.map((row, rowIndex) => (
          <tr key={rowIndex}>
            {columns.map((column, columnIndex) => (
              <td key={`${rowIndex}-${columnIndex}`}>
                {column.element(row)}
              </td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

💡 A parte de CSS não está no escopo desse post, então se sinta livre para estilizar a table!


Enfim, esse foi um review básico sobre componentes de tipagens genérica usando React, espero ter ajudado!

Se precisarem tirar alguma dúvida eu fico feliz em responder e bora codar 🚀

Carregando publicação patrocinada...
2
1