Im using Material UI's datagrid to show some data in a table, I recently switched over to RTK query from redux for caching and to get rid of the global state management in the project but encountered a problem as mentioned in the title.
The error that I'm currently seeing is TypeError: Cannot read properties of undefined (reading 'name')
This occurs when trying to get data in a nested object, I'm using valueGetter
to get to the nested value. The const below shows the columns of the table and where valueGetter
is being used (I've removed some of the columns for this example)
const columns = [
{ field: "id", headerName: "ID", width: 70 },
{
field: "App",
headerName: "App",
width: 110,
valueGetter: (params) => params.row.app.name, // <---- Recieving error here
valueFormatter: (params) => capitalise(params.value),
}
];
This is what my component looks like (I've also removed some of the code just for this example):
import React, { useEffect, useState } from "react";
import { DataGrid } from "@mui/x-data-grid";
import { IconButton } from "@material-ui/core";
import { CustomLoadingOverlay } from "../gbl/datagrid/DataGridUtils";
import { capitalise } from "../utils/util";
import {
useGetAppAccountTypesQuery,
useDeleteAccountTypeMutation,
} from "../../services/reducers/accounttypes";
export default ({ appid }) => {
const { data, isLoading, isSuccess } = useGetAppAccountTypesQuery(appid);
const [loading, setLoading] = useState(true);
console.log(data, isLoading, isSuccess);
// Testing only
useEffect(() => {
if (isSuccess) setLoading(false);
}, [isLoading]);
const columns = [
{ field: "id", headerName: "ID", width: 70 },
{
field: "App",
headerName: "App",
width: 110,
valueGetter: (params) => params.row.app.name,
valueFormatter: (params) => capitalise(params.value),
}
];
return (
<>
<div
style={{
height: 190,
width: "100%",
backgroundColor: "white",
marginTop: 40,
borderRadius: 4,
}}
>
<DataGrid
loading={loading}
rows={data}
columns={columns}
components={{
LoadingOverlay: CustomLoadingOverlay,
}}
hideFooter
density="compact"
pageSize={6}
rowsPerPageOptions={[5]}
checkboxSelection
disableSelectionOnClick
></DataGrid>
</div>
</>
);
};
The component re-renders a couple of times, from each render the console.log(data, isLoading, isSuccess)
looks like
undefined true false <---- this gets logged 3 times
Array(2) false true
Array(2) false true
this issue seems to be intermittent as it does sometimes work,
Before switching to RTK query, I was using Axios and useEffect to call the endpoints and never received this error, am I doing something wrong here?
Any help would be appreciated.
I would say there is nothing to do with RTK-Q itself here, it's just a common JS problem\question, that should be narrowed to null-check. It's normal that in some conditions you may have an undefined result, and it should be treated in the code. There may be several options:
The most right way is to just return an empty object or loading state like spinner\skeleton, when data is undefined and\or data isLoading = true :
const { data, isLoading, isSuccess } = useGetAppAccountTypesQuery(appid);
if (!data || isLoading) return <>Skeleton or Spinner</>;
In general - use a null-safe ?
accessor:
valueGetter: (params) => params?.row?.app?.name,
Set the default value for data
with some object structure that should match your default state for proper column config:
const DEFAULT_DATA = { row: { app: { name: "" } } }
const { data = DEFAULT_DATA, isLoading, isSuccess } = useGetAppAccountTypesQuery(appid);
Just do nothing during the data loading, when data is undefined:
const columns = data ? [...your column mapping...] : [];
You can use them in combination, achieving the expected outcome during the loading time.