How to use CloneButton?

Clone page didn't render initial value
8 Replies
kapa.ai
kapa.ai5w ago
Hi @tingadev. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To use the CloneButton in your refine application, you need to follow these steps. The CloneButton component is available in various UI frameworks like Ant Design, Mantine, Chakra UI, and Material UI. Below are examples of how to use the CloneButton with different UI frameworks. Ant Design
import { List, useTable, CloneButton } from "@refinedev/antd";
import { Table } from "antd";

const PostList: React.FC = () => {
const { tableProps } = useTable<IPost>();

return (
<List>
<Table {...tableProps} rowKey="id">
<Table.Column dataIndex="id" title="ID" />
<Table.Column dataIndex="title" title="Title" width="100%" />
<Table.Column<IPost>
title="Actions"
dataIndex="actions"
key="actions"
render={(_, record) => (
<CloneButton size="small" recordItemId={record.id} />
)}
/>
</Table>
</List>
);
};

interface IPost {
id: number;
title: string;
}
import { List, useTable, CloneButton } from "@refinedev/antd";
import { Table } from "antd";

const PostList: React.FC = () => {
const { tableProps } = useTable<IPost>();

return (
<List>
<Table {...tableProps} rowKey="id">
<Table.Column dataIndex="id" title="ID" />
<Table.Column dataIndex="title" title="Title" width="100%" />
<Table.Column<IPost>
title="Actions"
dataIndex="actions"
key="actions"
render={(_, record) => (
<CloneButton size="small" recordItemId={record.id} />
)}
/>
</Table>
</List>
);
};

interface IPost {
id: number;
title: string;
}
Source (https://refine.dev/docs/ui-integrations/ant-design/components/buttons/clone-button#usage) Mantine
import { List, CloneButton } from "@refinedev/mantine";
import { Table, Pagination } from "@mantine/core";
import { useTable } from "@refinedev/react-table";
import { ColumnDef, flexRender } from "@tanstack/react-table";
const PostList: React.FC = () => {
const columns = React.useMemo<ColumnDef<IPost>[]>(() => [
{ id: "id", header: "ID", accessorKey: "id" },
{ id: "title", header: "Title", accessorKey: "title" },
{
id: "actions",
header: "Actions",
accessorKey: "id",
cell: function render({ getValue }) {
return <CloneButton size="xs" recordItemId={getValue() as number} />;
},
},
], []);

const { getHeaderGroups, getRowModel, refineCore: { setCurrent, pageCount, current } } = useTable({ columns });

List.defaultProps = { headerButtons: <></> };

return (
<List>
<Table>
<thead>
{getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id}>
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
</th>
))}
</tr>
))}
</thead>
<tbody>
{getRowModel().rows.map((row) => (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => (
<td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
</tbody>
</Table>
<br />
<Pagination position="right" total={pageCount} page={current} onChange={setCurrent} />
</List>
);
};
interface IPost {
id: number;
title: string;
}
import { List, CloneButton } from "@refinedev/mantine";
import { Table, Pagination } from "@mantine/core";
import { useTable } from "@refinedev/react-table";
import { ColumnDef, flexRender } from "@tanstack/react-table";
const PostList: React.FC = () => {
const columns = React.useMemo<ColumnDef<IPost>[]>(() => [
{ id: "id", header: "ID", accessorKey: "id" },
{ id: "title", header: "Title", accessorKey: "title" },
{
id: "actions",
header: "Actions",
accessorKey: "id",
cell: function render({ getValue }) {
return <CloneButton size="xs" recordItemId={getValue() as number} />;
},
},
], []);

const { getHeaderGroups, getRowModel, refineCore: { setCurrent, pageCount, current } } = useTable({ columns });

List.defaultProps = { headerButtons: <></> };

return (
<List>
<Table>
<thead>
{getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id}>
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
</th>
))}
</tr>
))}
</thead>
<tbody>
{getRowModel().rows.map((row) => (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => (
<td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
</tbody>
</Table>
<br />
<Pagination position="right" total={pageCount} page={current} onChange={setCurrent} />
</List>
);
};
interface IPost {
id: number;
title: string;
}
Source (https://refine.dev/docs/ui-integrations/mantine/components/buttons/clone-button#usage) Chakra UI ```jsx import { List, CloneButton } from "@refined ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai5w ago
<@761921731505946634> kapa.ai is still learning and improving, please let us know how it did by reacting below
adverse-sapphire
adverse-sapphire5w ago
@kapa.ai Clone page didn't render initial value
kapa.ai
kapa.ai5w ago
Let me check the knowledge base for you. One moment! :books: ---------------------- If your clone page didn't render the initial values, you need to ensure that the useForm hook is correctly set up to fetch and set the initial values from the record you are cloning. Here is an example of how to set up the clone page using the useForm hook: Example with useForm Hook
import React from "react";
import { useForm, HttpError, BaseKey } from "@refinedev/core";

interface IProduct {
id: BaseKey;
name: string;
material: string;
}

interface FormValues {
name?: string;
material?: string;
}

export const Clone: React.FC = () => {
const { queryResult, formLoading, onFinish } = useForm<IProduct, HttpError, FormValues>({
resource: "products",
action: "clone",
id: "123", // Replace with the actual ID you want to clone
redirect: "show", // Redirect to show page after form submission, defaults to "list"
});

const defaultValues = queryResult?.data?.data;
const [values, setValues] = React.useState<FormValues>({
name: defaultValues?.name || "",
material: defaultValues?.material || "",
});

React.useEffect(() => {
setValues({
name: defaultValues?.name || "",
material: defaultValues?.material || "",
});
}, [defaultValues]);

const onSubmit = (e) => {
e.preventDefault();
onFinish(values);
};
return (
<form onSubmit={onSubmit}>
<label htmlFor="name">Name</label>
<input
name="name"
placeholder="Name"
value={values.name}
onChange={(e) => setValues({ ...values, name: e.target.value })}
/>
<label htmlFor="material">Material</label>
<input
name="material"
placeholder="Material"
value={values.material}
onChange={(e) => setValues({ ...values, material: e.target.value })}
/>
<button type="submit">Submit</button>
</form>
);
};
import React from "react";
import { useForm, HttpError, BaseKey } from "@refinedev/core";

interface IProduct {
id: BaseKey;
name: string;
material: string;
}

interface FormValues {
name?: string;
material?: string;
}

export const Clone: React.FC = () => {
const { queryResult, formLoading, onFinish } = useForm<IProduct, HttpError, FormValues>({
resource: "products",
action: "clone",
id: "123", // Replace with the actual ID you want to clone
redirect: "show", // Redirect to show page after form submission, defaults to "list"
});

const defaultValues = queryResult?.data?.data;
const [values, setValues] = React.useState<FormValues>({
name: defaultValues?.name || "",
material: defaultValues?.material || "",
});

React.useEffect(() => {
setValues({
name: defaultValues?.name || "",
material: defaultValues?.material || "",
});
}, [defaultValues]);

const onSubmit = (e) => {
e.preventDefault();
onFinish(values);
};
return (
<form onSubmit={onSubmit}>
<label htmlFor="name">Name</label>
<input
name="name"
placeholder="Name"
value={values.name}
onChange={(e) => setValues({ ...values, name: e.target.value })}
/>
<label htmlFor="material">Material</label>
<input
name="material"
placeholder="Material"
value={values.material}
onChange={(e) => setValues({ ...values, material: e.target.value })}
/>
<button type="submit">Submit</button>
</form>
);
};
Key Points 1. Fetch Data: Ensure that the useForm hook is fetching the data correctly using the id of the record you want to clone. 2. Set Initial Values: Use React.useEffect to set the initial values of the form fields once the data is fetched. 3. Handle Form Submission: Use the onFinish method from useForm to handle form submission. By following these steps, you should be able to render the initial values correctly on your clone page. For more details, you can refer to the useForm documentation (https://refine.dev/docs/data/hooks/use-form#usage). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai5w ago
<@761921731505946634> kapa.ai is still learning and improving, please let us know how it did by reacting below
adverse-sapphire
adverse-sapphire5w ago
how to add clone page to resources? @kapa.ai
kapa.ai
kapa.ai5w ago
Let me check the knowledge base for you. One moment! :books: ---------------------- To add a clone page to your resources in a refine application, you need to define the clone component in your resource configuration. Below is an example of how to do this: Step-by-Step Guide 1. Define the Clone Component: Create a component for the clone page. This component will handle the cloning logic and form.
// CoachClone.tsx
import React from "react";
import { useForm, HttpError, BaseKey } from "@refinedev/core";

interface ICoach {
id: BaseKey;
name: string;
experience: string;
}

interface FormValues {
name?: string;
experience?: string;
}

export const CoachClone: React.FC = () => {
const { queryResult, formLoading, onFinish } = useForm<ICoach, HttpError, FormValues>({
resource: "coaches",
action: "clone",
id: "1", // Replace with the actual ID you want to clone
redirect: "show", // Redirect to show page after form submission, defaults to "list"
});

const defaultValues = queryResult?.data?.data;
const [values, setValues] = React.useState<FormValues>({
name: defaultValues?.name || "",
experience: defaultValues?.experience || "",
});

React.useEffect(() => {
setValues({
name: defaultValues?.name || "",
experience: defaultValues?.experience || "",
});
}, [defaultValues]);

const onSubmit = (e) => {
e.preventDefault();
onFinish(values);
};
return (
<form onSubmit={onSubmit}>
<label htmlFor="name">Name</label>
<input
name="name"
placeholder="Name"
value={values.name}
onChange={(e) => setValues({ ...values, name: e.target.value })}
/>
<label htmlFor="experience">Experience</label>
<input
name="experience"
placeholder="Experience"
value={values.experience}
onChange={(e) => setValues({ ...values, experience: e.target.value })}
/>
<button type="submit">Submit</button>
</form>
);
};
// CoachClone.tsx
import React from "react";
import { useForm, HttpError, BaseKey } from "@refinedev/core";

interface ICoach {
id: BaseKey;
name: string;
experience: string;
}

interface FormValues {
name?: string;
experience?: string;
}

export const CoachClone: React.FC = () => {
const { queryResult, formLoading, onFinish } = useForm<ICoach, HttpError, FormValues>({
resource: "coaches",
action: "clone",
id: "1", // Replace with the actual ID you want to clone
redirect: "show", // Redirect to show page after form submission, defaults to "list"
});

const defaultValues = queryResult?.data?.data;
const [values, setValues] = React.useState<FormValues>({
name: defaultValues?.name || "",
experience: defaultValues?.experience || "",
});

React.useEffect(() => {
setValues({
name: defaultValues?.name || "",
experience: defaultValues?.experience || "",
});
}, [defaultValues]);

const onSubmit = (e) => {
e.preventDefault();
onFinish(values);
};
return (
<form onSubmit={onSubmit}>
<label htmlFor="name">Name</label>
<input
name="name"
placeholder="Name"
value={values.name}
onChange={(e) => setValues({ ...values, name: e.target.value })}
/>
<label htmlFor="experience">Experience</label>
<input
name="experience"
placeholder="Experience"
value={values.experience}
onChange={(e) => setValues({ ...values, experience: e.target.value })}
/>
<button type="submit">Submit</button>
</form>
);
};
2. Add the Clone Component to Resources: Update your resource configuration to include the clone component.
// App.tsx or where you define your resources
import { Refine } from "@refinedev/core";
import { CoachList, CoachCreate, CoachEdit, CoachShow, CoachClone } from "./components";

const resources = [
{
name: "coaches",
list: CoachList,
create: CoachCreate,
edit: CoachEdit,
clone: CoachClone,
show: CoachShow,
meta: {
canDelete: true,
icon: <UserAddOutlined />,
},
},
// other resources
];

const App: React.FC = () => {
return (
<Refine
resources={resources}
// other props
/>
);
};
// App.tsx or where you define your resources
import { Refine } from "@refinedev/core";
import { CoachList, CoachCreate, CoachEdit, CoachShow, CoachClone } from "./components";

const resources = [
{
name: "coaches",
list: CoachList,
create: CoachCreate,
edit: CoachEdit,
clone: CoachClone,
show: CoachShow,
meta: {
canDelete: true,
icon: <UserAddOutlined />,
},
},
// other resources
];

const App: React.FC = () => {
return (
<Refine
resources={resources}
// other props
/>
);
};
Important Notes - Ensure that the clone property in the resource configuration points to the correct clone component. - The useForm hook should be correctly set up to fetch the initial data and handle form submission. By following these steps, you should be able to add a clone page to your resources and ensure it works correctly. For more information, you can refer to the useNavigation documentation (https://refine.dev/docs/routing/hooks/use-navigation#clone) and the CloneButton documentation (https://refine.dev/docs/ui-integrations/ant-design/components/buttons/clone-button#resource). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai5w ago
<@761921731505946634> kapa.ai is still learning and improving, please let us know how it did by reacting below