reactjstypescriptmaterial-uimui-x-data-grid

How to extend a component to add default props in React/TS


I want to extend a component with React/TS (in my case is @mui/x-data-grid DataGrid) to fill the classes prop with my own app classes.

The way I first tought was to create a component, for example CustomDataGrid, like this:

import React from 'react';
import {DataGrid} from '@mui/x-data-grid';

const CustomDataGrid = (props) => {
  return <DataGrid {...props} classes={{root: 'my-root-class'}} />;
};

export default CustomDataGrid;

The problem is that I'm using typescript and I want to maintain the type ability to autocomplete in the IDE.

I also tried to define the type of my component as React.FC< Props>, but take a look at the DataGrid code:

declare const DataGrid: React$1.MemoExoticComponent<React$1.ForwardRefExoticComponent<Omit<Partial<DataGridPropsWithDefaultValues> & DataGridPropsWithComplexDefaultValueBeforeProcessing & DataGridPropsWithoutDefaultValue, DataGridForcedPropsKey> & {
    pagination?: true | undefined;
} & React$1.RefAttributes<HTMLDivElement>>>;

I don't know what to fill in the generic arguments to type the props.

Does anyone have an idea of how to do this without losing the autocomplete feature?


Solution

  • You can do that by using the React.ComponentProps¹ utility type to get the type of the props of DataGrid, and TypeScript's Omit to remove classes from it:

    import React from "react";
    import {DataGrid} from "@mui/x-data-grid";
    
    const CustomDataGrid = (props: Omit<React.ComponentProps<typeof DataGrid>, "classes">) => {
        return <DataGrid {...props} classes={{root: "my-root-class"}} />;
    };
    

    Here's a breakdown of Omit<Parameters<typeof DataGrid>[0], "classes">:

    Playground example

    If that last bullet point isn't accurate and you do want to allow people to specify classes, you can remove the Omit<..., "classes"> part, but be sure in that case to combine any props.classes specified by the caller with your hardcoded ones; the code as it is now would override theirs with yours. For instance, something like:

    const myRootClass = "my-root-class";
    const CustomDataGrid = (props: React.ComponentProps<typeof DataGrid>) => {
        const classes = props.classes ?? {};
        if (classes.root) {
            classes.root += ` ${myRootClass}`;
        } else {
            classes.root = myRootClass;
        }
        return <DataGrid {...props} classes={classes} />;
    };
    

    ¹ There's a note in the inline documentation of ComponentProps saying:

    NOTE: prefer ComponentPropsWithRef, if the ref is forwarded, or ComponentPropsWithoutRef when refs are not supported.

    ...so you may want the appropriate one of those instead of ComponentProps itself.