Home > OS >  Passing a type to a React component throws an error - react Typescript
Passing a type to a React component throws an error - react Typescript

Time:09-16

I am using ag-grid and I would like to pass the type of the data the component expects. When I pass in a type, I get an error message:

Expected 0 type arguments, but got 1. The component composes AgGridReact using forwardRef.

How do I pass in a type without encountering an error or how do I modify the Table component to accept generic types?

This is my code:

App.tsx

import * as React from 'react';
import { AgGridReact } from 'ag-grid-react';
import { ColDef, ColGroupDef } from 'ag-grid-enterprise';
import { GridOptions } from 'ag-grid-community';

import {
  FirstDataRenderedEvent,
  GridReadyEvent,
} from 'ag-grid-community/dist/lib/events';
import Table from './Table';
import './style.scss';

export interface ITerritoriesByZip {
  TERR_ID: string;
  TERR_NAME: string;
  FULL_NAME: string;
  WORK_EMAIL: string;
  OFFICE_PHONE: string;
  MOBILE_PHONE: string;
  ADDRESS1: string;
  CITY: string;
  STATE: string;
  ZIP: string;
}

export const columnDefs: (ColDef | ColGroupDef)[] = [
  {
    headerName: 'TERR_ID',
    field: 'TERR_ID',
  },
  {
    headerName: 'TERR_NAME',
    field: 'TERR_NAME',
  },
  {
    headerName: 'Full Name',
    field: 'FULL_NAME',
  },
  {
    headerName: 'Work Email',
    field: 'WORK_EMAIL',
  },
  {
    headerName: 'Office Phone',
    field: 'OFFICE_PHONE',
  },
  {
    headerName: 'Mobile Phone',
    field: 'MOBILE_PHONE',
  },
  {
    headerName: 'Address 1',
    field: 'ADDRESS1',
  },
  {
    headerName: 'City',
    field: 'CITY',
  },
  {
    headerName: 'State',
    field: 'STATE',
  },
  {
    headerName: 'ZIP',
    field: 'ZIP',
  },
];

const gridOptions: GridOptions = {
  alignedGrids: [],
  defaultColDef: {
    editable: true,
    sortable: true,
    resizable: true,
    filter: false,
    flex: 1,
    minWidth: 100,
  },
};

export default function App() {
  const currentMainGridRef = React.useRef<AgGridReact<ITerritoriesByZip>>(null);

  const data = [];

  const onGridReady = React.useCallback((params: GridReadyEvent) => {
    params.api.sizeColumnsToFit();
  }, []);

  const onCurrentFirstDataRendered = React.useCallback(
    (params: FirstDataRenderedEvent) => {
      params.api.sizeColumnsToFit();
    },
    []
  );

  return (
    <div>
      <h1>Hello StackBlitz!</h1>
      <p>Start editing to see some magic happen :)</p>
      <Table<ITerritoriesByZip>
        ref={currentMainGridRef}
        rowData={data}
        rowCount={data.length}
        columnDefs={columnDefs}
        isLoading={false}
        isSuccess={true}
        pagination={false}
        onGridReady={onGridReady}
        onFirstDataRendered={onCurrentFirstDataRendered}
        gridOptions={gridOptions}
      />
    </div>
  );
}

Table.tsx

import { Box, LinearProgress, Typography } from '@mui/material';
import React, {
  ForwardedRef,
  forwardRef,
  useEffect,
  useMemo,
  useState,
} from 'react';
import { AgGridReact } from 'ag-grid-react';
import {
  AgGridReactProps,
  AgReactUiProps,
} from 'ag-grid-react/lib/shared/interfaces';
import { has, isEmpty, isFunction, isNull, map } from 'lodash';
import { renderToStaticMarkup } from 'react-dom/server';
import { GridApi } from 'ag-grid-community/dist/lib/gridApi';
import { ColumnApi } from 'ag-grid-community/dist/lib/columns/columnApi';
import { GridOptions } from 'ag-grid-community';
import { ColumnState } from 'ag-grid-community/dist/lib/columns/columnModel';
import {
  ColDef,
  ColGroupDef,
} from 'ag-grid-community/dist/lib/entities/colDef';
import { GridReadyEvent } from 'ag-grid-community/dist/lib/events';
import CustomPagination from './PaginationRenderer';

interface ITable<T> extends AgGridReactProps, AgReactUiProps {
  isLoading: boolean;
  isSuccess: boolean;
  id?: string;
  pagination: boolean;
  rowData?: T[] | null;
  title?: string;
  gridOptions?: GridOptions;
  rowCount: number;
  columnDefs?: (ColDef | ColGroupDef)[] | null;
  onGridReady?: (params: GridReadyEvent) => void;
}

export interface AutoSizeRef {
  autoSizeAll: () => void;
}

const AgGridTable = <T extends {}>(
  props: ITable<T>,
  ref: ForwardedRef<AgGridReact<T>>
) => {
  const [colDefState, setColDefState] = useState<ColumnState[]>([]);
  const autoSizeAll = (): void => {
    if (ref != null && !isFunction(ref)) {
      const allColumnIds = map(
        (ref.current?.columnApi as ColumnApi).getColumns(),
        (column) => {
          return (column as any).getId();
        }
      );
      (ref.current?.columnApi as ColumnApi).autoSizeColumns(
        allColumnIds,
        false
      );
    }
  };

  // useImperativeHandle(ref, () => ({
  //   autoSizeAll,
  // }));

  useEffect(() => {
    if (
      ref != null &&
      !isFunction(ref) &&
      !isNull(ref.current) &&
      !isEmpty(ref.current) &&
      has(ref.current, 'api')
    ) {
      setTimeout(() => autoSizeAll(), 400);
    }
  }, [ref]);

  const containerStyle = useMemo(() => ({ width: '100%', height: '100%' }), []);
  const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);

  const defaultExcelExportParams = useMemo(() => {
    return {
      allColumns: true,
    };
  }, []);

  const { isLoading, pagination, id, isSuccess, onGridReady, ...rest } = props;

  useEffect(() => {
    if (ref != null && !isFunction(ref)) {
      if (
        isLoading &&
        !isNull(ref.current) &&
        !isEmpty(ref.current) &&
        has(ref.current, 'api')
      ) {
        (ref.current?.api as GridApi).showLoadingOverlay();
      }
      if (
        isEmpty(props.rowData) &&
        !isNull(ref.current) &&
        !isEmpty(ref.current) &&
        has(ref.current, 'api')
      ) {
        (ref.current?.api as GridApi).showNoRowsOverlay();
      }
    }
  }, [isLoading, ref, props.rowData]);

  const onTableGridReady = (params: GridReadyEvent) => {
    setColDefState(params.columnApi.getColumnState());
  };

  return (
    <Box className="pt-2" style={containerStyle}>
      {isLoading && <LinearProgress variant="indeterminate" />}
      <Box className="ag-theme-alpine" id={id} style={gridStyle}>
        <AgGridReact<T>
          animateRows
          ref={ref}
          pagination={pagination}
          domLayout="autoHeight"
          paginationPageSize={100}
          onGridReady={(params) => {
            onTableGridReady(params);
            if (onGridReady) {
              onGridReady(params);
            }
          }}
          suppressPaginationPanel
          statusBar={{
            statusPanels: [
              {
                statusPanel: CustomPagination,
                statusPanelParams: {
                  dataCount: props.rowCount,
                  paginationEnabled: pagination,
                },
              },
            ],
          }}
          gridOptions={props.gridOptions}
          defaultExcelExportParams={defaultExcelExportParams}
          overlayLoadingTemplate={
            '<span >Please wait while your rows are loading</span>'
          }
          {...rest}
        />
      </Box>
    </Box>
  );
};

const Table = forwardRef(AgGridTable);

export default Table;

Stackblitz Link: Link.

Any help is appreciated.

CodePudding user response:

in Table.tsx do the following:

const TableRef = forwardRef(AgGridTable);

const Table = <T extends {}>({
    myRef,
    ...rest
}: ITable<T> & { myRef: ForwardedRef<AgGridReact<T>> }) => (
   <TableRef {...rest} ref={myRef} />
);

export default Table;

Check the fork

CodePudding user response:

Another option is to use wrapper:

const AgGridTable = <T extends {}>() => forwardRef<AgGridReact<T>, ITable<T>>((props, ref) => {...}

const Table = AgGridTable();

export default Table;
  • Related