发布于 2026-01-06 9 阅读
0

迁移 React-Admin 应用以进行改进💖

迁移 React-Admin 应用以进行改进💖

refine是一个基于Ant Design 的React框架,高度可定制,专为数据密集型应用而设计。它能够轻松处理许多应用中常见的 CRUD 功能。至于其他所需功能(CRUD 功能之外的),则需要您自行实现,就像其他React应用一样。

React-Admin是一个基于Material DesignMaterial UI的出色 B2B 应用框架。它提供可直接获取数据的组件,您只需将它们组合在一起即可创建应用程序。

refine与其他工具的不同之处在于它构建应用程序的方式。refine 直接提供Ant Design组件以及一些用于操作这些组件的钩子。钩子会为你提供 Ant Design 组件所需的属性。

这也是朝着实现无头目标迈出的一步。

要了解更多关于 Refine 的信息,请参阅:https://refine.dev/docs/getting-started/overview

最近,我们团队决定将一位客户的 B2B 管理面板从 React-Admin 迁移到 Refine,以此来测试我们的新框架并提高工作效率。我的任务是负责迁移,我花了整整一天半的时间用 Refine 重写了这个面板。

迁移后资源菜单的优化
我们的面板有 7 个资源(全部可列出),其中 4 个必须有创建和编辑页面,6 个必须可以导出到.csv文件,并且其中一些资源有图像,所有图像都必须以base64格式上传。

这是迁移前的样子(React-Admin):

React-Admin 中的管理面板

迁移后(优化)效果如下:

精简版管理面板

这两张图片都显示了资源(活动)的列表页面。

迁移列表页面

列表页面包含一个或多个表格。理想情况下,所有表格状态都应该由所使用的框架进行管理。

refine 函数对表格的处理非常灵活。你可以将表格放置在任何位置,并使用useTable 函数进行任意配置。请参阅fineFoods 示例及其代码

以下是 React-Admin 的一个示例列表页面,它显示了来自资源API 端点的列表,包括idnameisActivestartDateendDatecampaigns

import React from "react";
import {
  List as ReactAdminList,
  Datagrid,
  TextField,
  BooleanField,
  EditButton
} from "react-admin";

import LocalizeDateField from '../../fields/LocalizeDateField'; 

const List = (props) => (
  <ReactAdminList {...props}>
    <Datagrid>
      <TextField source="id" label="ID" />
      <TextField source="name" label="Name" />
      <BooleanField source="isActive" label="Active" />
      <LocalizeDateField source="startDate" />
      <LocalizeDateField source="endDate" />
      <EditButton basePath="/campaigns" />
    </Datagrid>
  </ReactAdminList>
);

export default List;
Enter fullscreen mode Exit fullscreen mode

看起来是这样的:

React-Admin 中的管理面板

以下是使用 refine 渲染相同列表的代码:

import React from "react";
import {
    List,
    Table,
    Space,
    Button,
    BooleanField,
    DateField,
    CreateButton,
    EditButton,
    ExportButton,
    Icons,
    useTable,
    getDefaultSortOrder,
    useExport,
    useDeleteMany,
    IResourceComponentsProps,
} from "@pankod/refine";

import { ICampaign } from "interfaces";

export const CampaignsList: React.FC<IResourceComponentsProps> = () => {
    const { tableProps, sorter } = useTable<ICampaign>({
        initialSorter: [
            {
                field: "id",
                order: "asc",
            },
        ],
    });

    const { isLoading: isExportLoading, triggerExport } = useExport();

    const [selectedRowKeys, setSelectedRowKeys] = React.useState<React.Key[]>(
        [],
    );

    const handleSelectChange = (selectedRowKeys: React.Key[]) => {
        setSelectedRowKeys(selectedRowKeys);
    };

    const rowSelection = {
        selectedRowKeys,
        onChange: handleSelectChange,
    };

    const { mutate, isLoading } = useDeleteMany<ICampaign>();

    const deleteSelectedItems = () => {
        mutate(
            {
                resource: "campaigns",
                ids: selectedRowKeys.map(String),
                mutationMode: "undoable",
            },
            {
                onSuccess: () => {
                    setSelectedRowKeys([]);
                },
            },
        );
    };

    const hasSelected = selectedRowKeys.length > 0;

    return (
        <List pageHeaderProps={{
            subTitle: hasSelected && (
                <Button
                    type="text"
                    onClick={() => deleteSelectedItems()}
                    loading={isLoading}
                    icon={
                        <Icons.DeleteOutlined
                            style={{ color: "green" }}
                        />
                    }
                >
                    Delete
                </Button>
            ),
            extra: (
                <Space>
                    <CreateButton />
                    <ExportButton
                        onClick={triggerExport}
                        loading={isExportLoading}
                    />
                </Space>
            ),
        }}>
            <Table {...tableProps} rowSelection={rowSelection} rowKey="id">
                <Table.Column
                    dataIndex="id"
                    title="ID"
                    sorter
                    defaultSortOrder={getDefaultSortOrder("id", sorter)}
                    width="70px"
                />
                <Table.Column
                    dataIndex="name"
                    title="Name"
                    sorter
                    defaultSortOrder={getDefaultSortOrder("name", sorter)}
                />
                <Table.Column
                    dataIndex="isActive"
                    title="Active"
                    render={(isActive) => <BooleanField value={isActive} />}
                    sorter
                    defaultSortOrder={getDefaultSortOrder("isActive", sorter)}
                />
                <Table.Column
                    dataIndex="startDate"
                    title="Start Date"
                    render={(value) => (
                        <DateField value={value} format="LLL" />
                    )}
                    sorter
                    defaultSortOrder={getDefaultSortOrder("startDate", sorter)}
                />
                <Table.Column
                    dataIndex="endDate"
                    title="End Date"
                    render={(value) => (
                        <DateField value={value} format="LLL" />
                    )}
                    sorter
                    defaultSortOrder={getDefaultSortOrder("endDate", sorter)}
                />
                <Table.Column<ICampaign>
                    fixed="right"
                    title="Actions"
                    dataIndex="actions"
                    render={(_, { id }) => (
                        <EditButton recordItemId={id} />
                    )}
                />
            </Table>
        </List>
    );
};
Enter fullscreen mode Exit fullscreen mode

代码比较长,因为我们必须手动处理选择和批量删除按钮。这是因为 refine 也与 Ant Design 组件的代码解耦了。但它的优势在于你可以使用 Ant Design。你可以随意使用 Ant Design 的表格,然后将表格数据与 refine 连接起来。关键在于可定制性。效果
如下:

精简版管理面板

在 refine 中,我们使用了 Ant Design 的Table组件。

迁移创建/编辑页面

在 React-Admin 中,资源创建页面的代码如下所示:

import React from "react";
import {
  required,
  Create as ReactAdminCreate,
  SimpleForm,
  BooleanInput,
  TextInput,
  DateTimeInput
} from "react-admin";

const Create = (props: any) => (
  <ReactAdminCreate {...props}>
    <SimpleForm>
      <TextInput fullWidth variant="outlined" source="name" validate={[required()]} />
      <BooleanInput fullWidth variant="outlined" source="isActive" label="Active" />
      <DateTimeInput
        source="startDate"
        label="Start Date"
        validate={[required()]}
        fullWidth variant="outlined"
      />
      <DateTimeInput
        source="endDate"
        label="End Date"
        validate={[required()]}
        fullWidth variant="outlined"
      />
    </SimpleForm>
  </ReactAdminCreate>
);

export default Create;
Enter fullscreen mode Exit fullscreen mode

它看起来是这样的:

React-Admin 创建页面示例

为了更精确地描述,我们活动创建页面的代码如下所示:

import {
    Create,
    DatePicker,
    Form,
    Input,
    IResourceComponentsProps,
    Switch,
    useForm,
} from "@pankod/refine";
import dayjs from "dayjs";

export const CampaignsCreate: React.FC<IResourceComponentsProps> = () => {
    const { formProps, saveButtonProps } = useForm();

    return (
        <Create saveButtonProps={saveButtonProps}>
            <Form
                {...formProps}
                layout="vertical"
                initialValues={{ isActive: false }}
            >
                <Form.Item
                    label="Name"
                    name="name"
                    rules={[
                        {
                            required: true,
                        },
                    ]}
                >
                    <Input />
                </Form.Item>
                <Form.Item
                    label="Is Active"
                    name="isActive"
                    valuePropName="checked"
                >
                    <Switch />
                </Form.Item>
                <Form.Item
                    label="Start Date"
                    name="startDate"
                    rules={[
                        {
                            required: true,
                        },
                    ]}
                    getValueProps={(value) => dayjs(value)}
                >
                    <DatePicker />
                </Form.Item>
                <Form.Item
                    label="End Date"
                    name="endDate"
                    rules={[
                        {
                            required: true,
                        },
                    ]}
                    getValueProps={(value) => dayjs(value)}
                >
                    <DatePicker />
                </Form.Item>
            </Form>
        </Create>
    );
};
Enter fullscreen mode Exit fullscreen mode

在 refine 和 React-Admin 中,默认情况下,新建资源页面的代码和资源编辑页面的代码之间并没有太大区别。

另请注意,无论是 refine 还是 React-Admin,所有这些功能都是可自定义的。这些代码示例和屏幕截图意味着在资源列表/创建/编辑页面中几乎不需要额外的自定义。

refine 的优势在于您可以直接使用 Ant Design。假设您已经形成了自己独特的 Ant Design 应用程序使用方式,refine 不会干扰您的操作。相反,它会为您的 Ant Design 应用程序提供必要的数据。这样,refine 让您可以完全自由地根据需要自定义所有组件。

使用 refine 进行愉快编程 🪄

GitHub 标志 refinedev /精炼

一个 React 框架,用于构建内部工具、管理面板、仪表盘和 B2B 应用程序,具有无与伦比的灵活性。





Refine 是一款开源的 React 元框架,面向企业级应用,它完美地平衡了低代码/无代码和“从零开始”这两种模式,尤其适用于 CRUD 操作繁重的应用。Refine
提供无头解决方案,涵盖从管理面板到仪表盘和内部工具等方方面面。

惊人的 OpenSSF最佳实践 npm 版本 贡献者契约

Discord Twitter关注

如何精炼

什么是 Refine?

Refine是一个面向 CRUD 操作繁重的 Web 应用程序的 React 元框架。它可满足各种企业用例的需求,包括内部工具、管理面板、仪表盘和 B2B 应用程序。

Refine 的核心钩子和组件通过为项目的关键方面提供行业标准解决方案来简化开发过程,包括身份验证访问控制路由网络状态管理和国际化 (i18n )。

Refine 的无头架构通过将业务逻辑与 UI 和路由解耦,实现了高度可定制化的应用程序构建。这使得它可以与以下系统集成:

  • 任何自定义设计或 UI 框架,例如TailwindCSS,以及内置支持……





文章来源:https://dev.to/refine/migration-a-react-admin-application-to-refine-2j6k