Search code examples
reactjstypescriptmui-x-data-gridmui-x

How to use the MUI X DataGrid API in a TypeScript application that is not fully React based


I need to use the MUI X DatGrid in a TypeScript web application that is not fully react based but sometimes only uses individual React components.

The following example code shows a prototype on how to create a functional API.

The idea is to use the GridApi to functionally interact with the grid component but the code seems pretty ugly and I'm wondering:

  • is there a smarter way to do this?
  • why do i need the setTimeout before I can retrieve the GridApi?
  • is there a way to force the DataGrid to refresh without changing any properties?
import * as React from 'react';
import ReactDOM from 'react-dom/client';
import {StyledEngineProvider} from '@mui/material/styles';
import {DataGrid, useGridApiRef} from '@mui/x-data-grid';

import type {GridApi} from '@mui/x-data-grid';
export type apiRefType = React.MutableRefObject<GridApi>;

type GridPropsType = {
  hideFooter: boolean,
  setApiRef: (ref: apiRefType) => void,
};


const Grid = ({setApiRef}: GridPropsType): JSX.Element => {
    const apiRef = useGridApiRef();

    setApiRef(apiRef);

    return (
        <React.StrictMode>
            <StyledEngineProvider injectFirst>
                <div style={{height: '100%', width: '100%'}}>
                    <DataGrid columns={[]} rows={[]}apiRef={apiRef} />
                </div>
            </StyledEngineProvider>
        </React.StrictMode>
    );
};

export const renderGrid = async (target: HTMLElement, hideFooter: boolean): Promise<apiRefType> => {
    let apiRef: apiRefType | null = null;
    const setApiRef = (ref: apiRefType): void => {
        apiRef = ref;
    };

    return new Promise((resolve) => { // eslint-disable-line promise/avoid-new
        ReactDOM.createRoot(target).render(<Grid hideFooter={hideFooter} setApiRef={setApiRef}/>);
        setTimeout(() => {
            if (apiRef === null) {
                throw new Error('apiref was not initialized');
            }

            resolve(apiRef);
        }, 0);
    });
};


Solution

  • Olivier Tassinari offers an excellent solution in this SR

    import { renderGrid } from "./demo";
    
    const cols = [{ field: "username" }, { field: "age" }];
    const rows = [{ id: 1, username: "@MUI", age: 20 }];
    
    (async () => {
      const target = document.getElementById("root");
      const gridApi = await renderGrid(target, {
        hideFooter: true
      });
      gridApi.current.updateColumns(cols);
      gridApi.current.updateRows(rows);
    })();
    
    import * as React from "react";
    import * as ReactDOM from "react-dom/client";
    import { StyledEngineProvider } from "@mui/material/styles";
    import { DataGrid, DataGridProps, useGridApiRef, GridApi } from "@mui/x-data-grid";
    
    type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
    
    export type apiRefType = React.MutableRefObject<GridApi>;
    
    type GridPropsType = {
      options: Optional<DataGridProps, "rows" | "columns">;
      setApiRef: (ref: apiRefType) => void;
    };
    
    const Grid = ({ setApiRef, options }: GridPropsType): JSX.Element => {
      const apiRef = useGridApiRef();
    
      React.useLayoutEffect(() => {
        setApiRef(apiRef);
      }, [setApiRef, apiRef]);
    
      return (
        <React.StrictMode>
          <StyledEngineProvider injectFirst>
            <div style={{ height: "100%", width: "100%" }}>
              <DataGrid columns={[]} rows={[]} apiRef={apiRef} {...options} />
            </div>
          </StyledEngineProvider>
        </React.StrictMode>
      );
    };
    
    export const renderGrid = async (
      target: HTMLElement,
      options: GridPropsType["options"]
    ): Promise<apiRefType> => {
      return new Promise((resolve) => {
        // eslint-disable-line promise/avoid-new
        ReactDOM.createRoot(target).render(
          <Grid options={options} setApiRef={resolve} />
        );
      });
    };