1. Adding List Page
In Unit 2.4, we created the CRUD pages automatically with Inferencer. In this unit, we will create the CRUD pages manually using the codes generated by Inferencer. So, you can customize the pages as you wish.
Creating List Page
First, let's create our file under the src/pages/blog-posts
folder. We will name it list.tsx
. Then, we will copy the list page code generated by Inferencer and paste it into the file.
To copy the code and paste it into the file, follow the steps below:
-
Navigate to the localhost:3000/blog-posts in your browser.
-
Click on the "Show Code" button in the bottom right corner of the page.
-
You can see the list page code generated by Inferencer. Click on the "Copy" button to copy the code.
-
Paste the code into the you created,
list.tsx
file.
You can see the list page code generated by Inferencer below:
Instead of coding the list page component from scratch, Inferencer created the required code base on API response, so that we can customize.
Understanding the List Component
We will go through the list page components and hooks one by one.
-
<List/>
is a refine component that is used to presentation purposes like showing create button or page title etc.Refer to the
<List/>
documentation for more information → -
<Table/>
is a native Ant Design component. It renders records row by row as a table.<Table/>
expects arowKey
prop as the unique key of the records. In the auto-generated list page code, Inferencer usedid
field as therowKey
.Refer to the Ant Design
<Table/>
documentation for more information → -
useTable
hook returns the values needed by the<Table/>
component in thetableProps
variable.This is the point where the ✨real magic✨ happens!
useTable
hook fetches data from API and wraps them with various helper hooks required for the<Table/>
component. Data interaction functions like sorting, filtering, and pagination will be instantly available on the<Table/>
with this single line of code.Refer to the
useTable
documentation for more information → -
<Table.Column/>
is a native Ant Design component. It renders a column in the table.-
dataIndex
prop is used to specify the field of the record that will be rendered in this column. -
render
prop is used to render custom content in the column. In the auto-generated list page code, Inferencer used therender
prop to render thedescription
field using<MarkdownField/>
component.Refer to the
<MarkdownField/>
and other field components documentation for more information →
-
-
<EditButton/>
and<ShowButton/>
are refine components that are used to navigate to the edit and show pages of the record.Refer to the
<EditButton/>
documentation for more information →Refer to the
<ShowButton/>
documentation for more information →
Handling Relationships
Each blog post includes the category
field which has id
property. This is a foreign key that points to the categories
resource which is different than "blog_post" resource.
There is a title
field In the categories
resource. To display the category title
in the table, we can use the useMany
hook provided by refine.
This hook allows us to fetch data for multiple records in a single request by providing the id
's of the related records. In this case, we need to provide the id
's of the blog_posts categories. It is particularly useful when we need to fetch related data for multiple records.
Refer to the useMany
documentation for more information →
In this tutorial, each blog post record has a category
field as below:
{
...
"category": {
"id": 1
}
...
},
{
...
"category": {
"id": 2
}
...
}
We can use the useMany
hook to fetch the full category records for each of these blog posts, like this:
import { useMany } from '@refinedev/core'
const { data } = useMany({
resource: 'categories',
ids: blogPosts.map((blogPost) => blogPost.category.id),
})
This will pass the resource
and ids
to the dataProvider
's getMany
function. The dataProvider
will then make a single request to the API to fetch the full records for each category related to the blog posts. The resulting data
variable will be an array of category records, like this:
;[
{
id: 1,
title: 'mock category title',
},
{
id: 2,
title: 'another mock category title',
},
]
We can then use this data
array to display the title
of each category in the table.
Adding the List Page to the App
Now that we have created the list page, we need to add it to the App.tsx
file.
-
Open
src/App.tsx
file on your editor. -
Import the created
BlogPostList
component. -
Replace the
AntdInferencer
component with theBlogPostList
component.
import {
ErrorComponent,
ThemedLayout,
RefineThemes,
notificationProvider,
} from '@refinedev/antd'
import { Refine } from '@refinedev/core'
import { AntdInferencer } from '@refinedev/inferencer/antd'
import routerBindings, { NavigateToResource } from '@refinedev/react-router-v6'
import dataProvider from '@refinedev/simple-rest'
import { BrowserRouter, Outlet, Route, Routes } from 'react-router-dom'
import { BlogPostList } from 'pages/blog-posts/list'
import { ConfigProvider } from 'antd'
import '@refinedev/antd/dist/reset.css'
const App: React.FC = () => {
return (
<BrowserRouter>
<ConfigProvider theme={RefineThemes.Blue}>
<Refine
routerProvider={routerBindings}
dataProvider={dataProvider('https://api.fake-rest.refine.dev')}
notificationProvider={notificationProvider}
resources={[
{
name: 'blog_posts',
list: '/blog-posts',
show: '/blog-posts/show/:id',
create: '/blog-posts/create',
edit: '/blog-posts/edit/:id',
},
]}
>
<Routes>
<Route
element={
<ThemedLayout>
<Outlet />
</ThemedLayout>
}
>
<Route
index
element={<NavigateToResource resource="blog_posts" />}
/>
<Route path="blog-posts">
<Route index element={<BlogPostList />} />
<Route path="show/:id" element={<AntdInferencer />} />
<Route path="edit/:id" element={<AntdInferencer />} />
<Route path="create" element={<AntdInferencer />} />
</Route>
<Route path="*" element={<ErrorComponent />} />
</Route>
</Routes>
</Refine>
</ConfigProvider>
</BrowserRouter>
)
}
export default App
Now, we can see the list page in the browser at localhost:3000/blog-posts