Skip to main content

4.1. Adding List Page

In Unit 2.4, we saw that the CRUD pages in our React admin panel are coded automatically with Inferencers. In this unit, we take those generated page components, add them to the pages and make some customization ourselves.

In this post we cover the blog_posts list page.

The List Page

The Inferencer-generated <BlogPostList /> component is available at the / or the /blog-posts route. When we visit this page, we can view the code in a modal by clicking on Show the auto-generated code button:


react admin panel

The component looks like this:

Show generated code

import React from "react";
import {
useDataGrid,
EditButton,
ShowButton,
List,
MarkdownField,
DateField,
} from "@refinedev/mui";
import { DataGrid, GridColumns } from "@mui/x-data-grid";
import { useMany } from "@refinedev/core";

export const BlogPostList = () => {
const { dataGridProps } = useDataGrid();

const { data: categoryData, isLoading: categoryIsLoading } = useMany({
resource: "categories",
ids: dataGridProps?.rows?.map((item: any) => item?.category?.id) ?? [],
queryOptions: {
enabled: !!dataGridProps?.rows,
},
});

const columns = React.useMemo<GridColumns<any>>(
() => [
{
field: "id",
headerName: "Id",
type: "number",
minWidth: 50,
},
{
field: "title",
flex: 1,
headerName: "Title",
minWidth: 200,
},
{
field: "content",
flex: 1,
headerName: "Content",
minWidth: 250,
renderCell: function render({ value }) {
return (
<MarkdownField
value={(value ?? "").slice(0, 80) + "..."}
/>
);
},
},
{
field: "category",
flex: 1,
headerName: "Category",
valueGetter: ({ row }) => {
const value = row?.category?.id;

return value;
},
minWidth: 300,
renderCell: function render({ value }) {
return categoryIsLoading ? (
<>Loading...</>
) : (
categoryData?.data?.find((item) => item.id === value)
?.title
);
},
},
{
field: "status",
flex: 1,
headerName: "Status",
minWidth: 200,
},
{
field: "createdAt",
flex: 1,
headerName: "Created At",
minWidth: 250,
renderCell: function render({ value }) {
return <DateField value={value} />;
},
},
{
field: "actions",
headerName: "Actions",
sortable: false,
renderCell: function render({ row }) {
return (
<>
<EditButton hideText recordItemId={row.id} />
<ShowButton hideText recordItemId={row.id} />
</>
);
},
align: "center",
headerAlign: "center",
minWidth: 80,
},
],
[categoryData?.data]
);

return (
<List>
<DataGrid {...dataGridProps} columns={columns} autoHeight />
</List>
);
};

The code above was generated by Inferencer by carrying out an initial polling to the /blog-posts endpoint in Simple REST API. After polling, it figured out the shape of the API response and placed appropriate Material UI elements for presenting the data in a table.

Understanding the List Page Components

Let's briefly make sense of the components and hooks used in the list page.

Let's now copy this code and paste it into the file at src/pages/blog-posts/list.tsx. We should basically replace the existing code that uses the <MuiListInferencer />:

src/pages/blog-posts/list.tsx
// Remove this code and replace it with the Inferencer-generated code

import { IResourceComponentsProps } from "@refinedev/core";
import { MuiListInferencer } from "@refinedev/inferencer/mui";

export const BlogPostList: React.FC<IResourceComponentsProps> = () => {
return <MuiListInferencer />;
};

Handling Relationships

The blog_posts resource is associated with categories resource. Each blog post includes the category field as a foreign key with the id property.

In the <BlogPostList /> component above, we are using the useMany() hook to get the title field of the category associated with a blog post item:

useMany() hook in action
const { data: categoryData, isLoading: categoryIsLoading } = useMany({
resource: "categories",
ids: dataGridProps?.rows?.map((item: any) => item?.category?.id) ?? [],
queryOptions: {
enabled: !!dataGridProps?.rows,
},
});

refine useMany() Hook

The useMany() hook is good for fetching a collection of records in a single request. It has to be passed an array of the id's of related records. In this case, we are passing in the id's of the blog posts categories. The useMany() hook is useful for fetching associated data for multiple records.

Refer to the useMany() documentation for more information

Under the hood, the usemany() hook passes the resource and ids array to the dataProvider's getMany method. The dataProvider.getMany() method then fetches all the specified categories records in a single request to the API. In our React admin panel app, the successfully fetched data variable is an array of categories records that looks like this:

[
{
id: 1,
title: "mock category title",
},
{
id: 2,
title: "another mock category title",
},
];

We are then using this data array to filter and display the title of each category in the table.

List Page Preview

Now if we visit http://localhost:5173/blog-posts again, we can see the same blog list page in the browser.

This time, no Show the auto-generated code button is available as we did not use <MuiListInferencer />:

localhost:5173/blog-posts

From this point on, we can start customizing the pages to our liking. But for the brevity of the tutorial, we won't make any change here.

Checklist