KevinVandy / material-react-table

A fully featured Material UI V5 implementation of TanStack React Table V8, written from the ground up in TypeScript
https://material-react-table.com
MIT License
1.49k stars 428 forks source link

Error with accessorFn on nested props with v2.12.0 onwards #1205

Open ColinMorris83 opened 2 months ago

ColinMorris83 commented 2 months ago

material-react-table version

v2.13.1

react & react-dom versions

v18.3.1

Describe the bug and the steps to reproduce it

After going from v2.11.3 to any newer version, am getting an error during rendering of any table that has an accessorFn that uses nested props. e.g.

accessorFn: (row) => `${row.IdentityInfo.LastName}, ${row.IdentityInfo.FirstName}`,

Will error in the console about trying to call LastName on undefined.

Have narrowed it down in the sandbox to the use of enableFacetedValues and setting the isLoading property on the state, and having the table data set into the state inside a useEffect. This combination seems to cause the error. Taking away any of these stops the error. i.e remove the enableFacetedValues prop, or remove the isLoading prop from state, or have the items initially populated when component loads rather than after a useEffect has run.

import { useMemo, useEffect, useState } from "react";
import {
  MaterialReactTable,
  useMaterialReactTable,
  type MRT_ColumnDef,
} from "material-react-table";

//example data type
type Person = {
  name: {
    firstName: string;
    lastName: string;
  };
  address: string;
  city: string;
  state: string;
};

//nested data is ok, see accessorKeys in ColumnDef below
const data: Person[] = [
  {
    name: {
      firstName: "John",
      lastName: "Doe",
    },
    address: "261 Erdman Ford",
    city: "East Daphne",
    state: "Kentucky",
  },
  {
    name: {
      firstName: "Jane",
      lastName: "Doe",
    },
    address: "769 Dominic Grove",
    city: "Columbus",
    state: "Ohio",
  },
  {
    name: {
      firstName: "Joe",
      lastName: "Doe",
    },
    address: "566 Brakus Inlet",
    city: "South Linda",
    state: "West Virginia",
  },
  {
    name: {
      firstName: "Kevin",
      lastName: "Vandy",
    },
    address: "722 Emie Stream",
    city: "Lincoln",
    state: "Nebraska",
  },
  {
    name: {
      firstName: "Joshua",
      lastName: "Rolluffs",
    },
    address: "32188 Larkin Turnpike",
    city: "Omaha",
    state: "Nebraska",
  },
];

const pause = async (pauseTimeMilliseconds: number) =>
  await new Promise((resolve) => setTimeout(resolve, pauseTimeMilliseconds));

const Example = () => {
  const [items, setItems] = useState<Person[]>([]);
  const [isLoaded, setIsLoaded] = useState(false);
  useEffect(() => {
    // Simulate a fetch request
    pause(100).then(() => {
      setItems(data);
      setIsLoaded(true);
    });
  }, []);

  //should be memoized or stable
  const columns = useMemo<MRT_ColumnDef<Person>[]>(
    () => [
      {
        accessorFn: (row) => `${row.name.firstName} ${row.name.lastName}`,
        header: "Full Name",
        id: "FullName",
        size: 150,
      },
      {
        accessorKey: "name.lastName",
        header: "Last Name",
        size: 150,
      },
      {
        accessorKey: "address", //normal accessorKey
        header: "Address",
        size: 200,
      },
      {
        accessorKey: "city",
        header: "City",
        size: 150,
      },
      {
        accessorKey: "state",
        header: "State",
        size: 150,
      },
    ],
    [],
  );

  const table = useMaterialReactTable({
    columns,
    data: items, //data must be memoized or stable (useState, useMemo, defined outside of this component, etc.)
    enableFacetedValues: true,
    state: { isLoading: !isLoaded },
  });

  return <MaterialReactTable table={table} />;
};

export default Example;

Also worth noting it's not just accessorFn with an issue, even using a nested accessorKey like 'name.lastname' also causes errors in the console, although the table does at least still render in this case.

"name" in deeply nested key "name.lastName" returned undefined. 
    at MRT_TableHeadCellFilterLabel (https://q77l7n-3001.csb.app/node_modules/.vite/deps/material-react-table.js?v=0a060451:79416:9)
    at div
    at https://q77l7n-3001.csb.app/node_modules/.vite/deps/material-react-table.js?v=0a060451:2906:50
    at Box4 (https://q77l7n-3001.csb.app/node_modules/.vite/deps/material-react-table.js?v=0a060451:16235:19)
    at div
    at https://q77l7n-3001.csb.app/node_modules/.vite/deps/material-react-table.js?v=0a060451:2906:50

Minimal, Reproducible Example - (Optional, but Recommended)

https://codesandbox.io/p/devbox/boring-wilson-q77l7n

Screenshots or Videos (Optional)

No response

Do you intend to try to help solve this bug with your own PR?

No, because I do not know how

Terms

ColinMorris83 commented 1 week ago

Just to update, issue still exists with latest 3.0.1 version. And after a bit more experimenting, it might be related to the showing of skeleton in the loading state. If the state uses showLoadingOverlay there is no error:

state: { showLoadingOverlay: isFetching },

Whilst both of these will cause the error:

state: { isLoading: isFetching },
state: { showSkeletons: isFetching },