Skip to main content
Version: 4.xx.xx
Source Code

useEditableTable

useEditeableTable allows you to implement the edit feature on the <Table> with ease and returns properties that can be used on Ant Design's <Table> and <Form> components.

useEditeableTable hook is extended from the useTable hook from the @refinedev/antd package. This means that you can use all the features of useTable hook.

Usage​

Here is an example of how to use useEditableTable hook. We will explain the details of this example and hooks usage in the following sections.

localhost:3000/posts
import {
List,
SaveButton,
EditButton,
TextField,
useEditableTable,
} from "@refinedev/antd";
import { Table, Form, Space, Button, Input } from "antd";

interface IPost {
id: number;
title: string;
}

const PostList: React.FC = () => {
const {
tableProps,
formProps,
isEditing,
setId: setEditId,
saveButtonProps,
cancelButtonProps,
editButtonProps,
} = useEditableTable<IPost>();

return (
<List>
<Form {...formProps}>
<Table
{...tableProps}
rowKey="id"
onRow={(record) => ({
// eslint-disable-next-line
onClick: (event: any) => {
if (event.target.nodeName === "TD") {
setEditId && setEditId(record.id);
}
},
})}
>
<Table.Column dataIndex="id" title="ID" />
<Table.Column<IPost>
dataIndex="title"
title="Title"
render={(value, record) => {
if (isEditing(record.id)) {
return (
<Form.Item name="title" style={{ margin: 0 }}>
<Input />
</Form.Item>
);
}
return <TextField value={value} />;
}}
/>
<Table.Column<IPost>
title="Actions"
dataIndex="actions"
render={(_, record) => {
if (isEditing(record.id)) {
return (
<Space>
<SaveButton {...saveButtonProps} hideText size="small" />
<Button {...cancelButtonProps} size="small">
Cancel
</Button>
</Space>
);
}
return (
<EditButton
{...editButtonProps(record.id)}
hideText
size="small"
/>
);
}}
/>
</Table>
</Form>
</List>
);
};

Editing with buttons​

Let's say that we want to make the Post data where we show the id and title values a listing page:

This time, to add the edit feature, we have to cover the <Table> component with a <Form> component and pass the properties coming from useEditableTable to the corresponding components:

/pages/posts/list.tsx
import { List, useEditableTable, TextField } from "@refinedev/antd";
import { Table, Form } from "antd";

export const PostList: React.FC = () => {
const { tableProps, formProps } = useEditableTable<IPost>();

return (
<List>
<Form {...formProps}>
<Table {...tableProps} rowKey="id">
<Table.Column dataIndex="id" title="ID" />
<Table.Column dataIndex="title" title="Title" />
</Table>
</Form>
</List>
);
};

interface IPost {
id: number;
title: string;
}

Now lets add a column for edit buttons:

/pages/posts/list.tsx
import {
List,
SaveButton,
EditButton,
useEditableTable,
} from "@refinedev/antd";
import {
Table,
Form,
Space,
Button,
} from "antd";

export const PostList: React.FC = () => {
const {
tableProps,
formProps,
isEditing,
saveButtonProps,
cancelButtonProps,
editButtonProps,
} = useEditableTable<IPost>();

return (
<List>
<Form {...formProps}>
<Table {...tableProps} rowKey="id">
<Table.Column key="id" dataIndex="id" title="ID" />
<Table.Column key="title" dataIndex="title" title="Title" />
<Table.Column<IPost>
title="Actions"
dataIndex="actions"
key="actions"
render={(_text, record) => {
if (isEditing(record.id)) {
return (
<Space>
<SaveButton {...saveButtonProps} size="small" />
<Button {...cancelButtonProps} size="small">
Cancel
</Button>
</Space>
);
}
return (
<Space>
<EditButton {...editButtonProps(record.id)} size="small" />
</Space>
);
}}
/>
</Table>
</Form>
</List>
);
};

isEditing function that returns from useEditableTable lets us check whether a line is currently in edit mode or not.

For now, our post is not editable yet. If a post is being edited, we must show editable columns inside a <Form.Item> using conditional rendering:

/pages/posts/list.tsx
import {
List,
SaveButton,
EditButton,
TextField,
useEditableTable,
} from "@refinedev/antd";
import {
Table,
Form,
Space,
Button,
Input,
} from "antd";

export const PostList: React.FC = () => {
const {
tableProps,
formProps,
isEditing,
saveButtonProps,
cancelButtonProps,
editButtonProps,
} = useEditableTable<IPost>();

return (
<List>
<Form {...formProps}>
<Table {...tableProps} rowKey="id">
<Table.Column key="id" dataIndex="id" title="ID" />
<Table.Column<IPost>
key="title"
dataIndex="title"
title="Title"
render={(value, record) => {
if (isEditing(record.id)) {
return (
<Form.Item name="title" style={{ margin: 0 }}>
<Input />
</Form.Item>
);
}
return <TextField value={value} />;
}}
/>
<Table.Column<IPost>
title="Actions"
dataIndex="actions"
key="actions"
render={(_text, record) => {
if (isEditing(record.id)) {
return (
<Space>
<SaveButton {...saveButtonProps} size="small" />
<Button {...cancelButtonProps} size="small">
Cancel
</Button>
</Space>
);
}
return (
<Space>
<EditButton {...editButtonProps(record.id)} size="small" />
</Space>
);
}}
/>
</Table>
</Form>
</List>
);
};

With this, when a user clicks on the edit button, isEditing(lineId) will turn true for the relevant line. This will also cause <TextInput> to show up on the line that's being edited. When the editing is finished, a new value can be saved by clicking <SaveButton>.

Implementation Tips:

By giving the <Table.Column> component a unique render property, you can render the value in that column however you want.

For more information, refer to the <Table.Column> documentation β†’

Editing by clicking to row​

A line with the id value can be put to edit mode programmatically by using the setId function that returns from useEditableTable.

The onRow property of the <Table> component can be used to put a line to editing mode when it's clicked on. The function given to the onRow property is called every time one of these lines is clicked on, with the information of which line was clicked on.

We can use setId to put a line to edit mode whenever it's clicked on.

/pages/posts/list.tsx
import { List, TextField, useEditableTable } from "@refinedev/antd";
import { Table, Form, Input } from "antd";

export const PostList: React.FC = () => {
const { tableProps, formProps, isEditing, setId } = useEditableTable<IPost>();

return (
<List>
<Form {...formProps}>
<Table
{...tableProps}
key="id"
onRow={(record) => ({
onClick: (event: any) => {
if (event.target.nodeName === "TD") {
setId && setId(record.id);
}
},
})}
>
<Table.Column key="id" dataIndex="id" title="ID" />
<Table.Column<IPost>
key="title"
dataIndex="title"
title="Title"
render={(value, data: any) => {
if (isEditing(data.id)) {
return (
<Form.Item name="title" style={{ margin: 0 }}>
<Input />
</Form.Item>
);
}
return <TextField value={value} />;
}}
/>
</Table>
</Form>
</List>
);
};

Properties​

All useForm and useTable properties are available in useEditableTable. You can read the documentation of useForm and useTable for more information.

autoSubmitClose​

autoSubmitClose makes the table's row close after a successful submit. It is true by default.

For this effect, useEditableTable automatically calls the setId function with undefined after successful submit.

const editableTable = useEditableTable({
autoSubmitClose: false,
});

Return Values​

All useForm and useTable return values are available in useEditableTable. You can read the documentation of useForm and useTable for more information.

cancelButtonProps​

cancelButtonProps returns the props for needed by the <EditButton>.

By default, the onClick function is overridden by useEditableTable. Which will call useForm's setId function with undefined when called.

cancelButtonProps: () => ButtonProps;

editButtonProps​

editButtonProps takes id as a parameter and returns the props needed by the <EditButton>.

By default, the onClick function is overridden by useEditableTable. Which will call useForm's setId function with the given id when called.

editButtonProps: (id: BaseKey) => ButtonProps;

It also returns a function that takes an id as a parameter and returns the props for the edit button.

isEditing​

isEditing: (id: BaseKey) => boolean;

Takes a id as a parameter and returns true if the given BaseKey is equal to the selected useForm's id.

API​

Properties​

Type Parameters​

PropertyDescriptionTypeDefault
TQueryFnDataResult data returned by the query function. Extends BaseRecordBaseRecordBaseRecord
TErrorCustom error object that extends HttpErrorHttpErrorHttpError
TVariablesValues for params{}
TSearchVariablesValues for search params{}
TDataResult data returned by the select function. Extends BaseRecord. If not specified, the value of TQueryFnData will be used as the default value.BaseRecordTQueryFnData

Return values​

PropertyDescriptionType
searchFormPropsAnt Design <Form> propsFormProps<TSearchVariables>
tablePropsAnt Design <Table> propsTableProps<TData>
tableQueryResultResult of the react-query's useQueryQueryObserverResult<{`` data: TData[];`` total: number; },`` TError>
sorterCurrent sorting stateCrudSorting
filtersCurrent filters stateCrudFilters
formAnt Design <Form> instanceFormInstance
formPropsAnt Design <Form> propsFormProps
saveButtonPropsProps for a submit button{ disabled: boolean; onClick: () => void; }
cancelButtonPropsProps for a cancel button{ onClick: () => void; }
editButtonPropsProps for an edit button{ onClick: () => void; }
queryResultResult of the query of a recordQueryObserverResult<T>
mutationResultResult of the mutation triggered by submitting the formUseMutationResult<T>
formLoadingLoading state of form requestboolean
idRecord id for edit actionBaseKey
setIdid setterDispatch<SetStateAction< BaseKey | undefined>>
isEditingCheck if is editing(id: BaseKey) => boolean

Example​

Run on your local
npm create refine-app@latest -- --example table-antd-use-editable-table