mcpField
R
react-admin

react-admin

github.com/marmelab开发27.0k216 次安装今天更新
代码仓库

关于 react-admin

react-admin 是一份开发指南,介绍如何使用 React-admin 为 REST 或 GraphQL API 构建单页应用。内容涵盖 providers、hooks、路由、数据操作、授权和表单,并指导使用框架现有抽象。

主要功能

  1. 01通过 provider 访问 API
  2. 02数据查询与变更 hooks
  3. 03上下文与控制器 hooks
  4. 04CRUD 与自定义路由
  5. 05身份验证与访问控制
  6. 06表单与关联记录字段

适用场景

  1. 01构建 REST 或 GraphQL 管理应用
  2. 02为资源添加 CRUD 页面
  3. 03通过 provider 处理 API 数据
  4. 04实现授权与表单

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/marmelab/react-admin --skill react-admin

安装后在 Claude Code 中输入/react-admin确认已加载。

详细介绍

React-Admin 开发指南

React-admin 是一个用于构建基于 REST/GraphQL API 的单页应用的框架。它基于 React Query、react-hook-form、react-router 和 Material UI 构建,提供 150 多个组件和数十个 Hook。在编写自定义代码之前,请始终先检查 react-admin 是否已提供适用于该任务的组件或 Hook。完整文档:https://marmelab.com/react-admin/doc/

Providers(后端抽象)

React-admin 不会直接调用 API。所有通信都通过 providers 进行——这些适配器会将 react-admin 的标准化调用转换为特定 API 的请求。三种主要的 provider 是:

  • dataProvider:所有 CRUD 操作(getList、getOne、create、update、delete、getMany、getManyReference、updateMany、deleteMany)。请参阅 DataProviders 和 50 多个现有适配器。
  • authProvider:身份验证和授权。请参阅 Authentication。
  • i18nProvider:翻译(translate、changeLocale、getLocale)。

重要规则:切勿在组件中使用 fetch、axios 或直接发起 HTTP 调用。始终使用 data provider Hook。这样可以确保正确的缓存、加载状态、错误处理、身份验证和乐观渲染。

组合(避免巨型组件)

React-admin 采用组合而非配置的方式。通过传递子组件来覆盖行为,而不是设置数十个属性:

<Edit actions={<MyCustomActions />}>
    <SimpleForm>
        <TextInput source="title" />
    </SimpleForm>
</Edit>

若要自定义布局,请将自定义布局组件传递给 <Admin layout={MyLayout}>。若要自定义菜单,请将其传递给 <Layout menu={MyMenu}>。这种链式组合是有意设计的——请参阅 Architecture。

Context:主动获取,而非向下传递

React-admin 组件通过 React contexts 向后代组件提供数据。使用 Hook 访问数据,而不是逐层传递属性:

  • useRecordContext() — Show/Edit/Create 视图中的当前记录。请参阅 useRecordContext。
  • useListContext() — List 视图中的列表数据、筛选器、分页和排序。请参阅 useListContext。
  • useShowContext()、useEditContext()、useCreateContext() — 详情视图的页面级状态。
  • useTranslate() — 来自 i18nProvider 的翻译函数。
  • useGetIdentity() — 来自 authProvider 的当前用户。

优先使用 Hook,而非自定义组件

如果 react-admin 组件的 UI 不符合需求,请使用底层 Hook,而不是从头构建。控制器 Hook(名称以 use*Controller 开头)提供所有逻辑,但不包含 UI:

  • useListController() — 列表数据获取、筛选和分页逻辑
  • useEditController() — 编辑表单的数据获取和提交逻辑
  • useShowController() — 展示页面的数据获取逻辑

路由

<Resource> 会自动声明 CRUD 路由(/posts、/posts/create、/posts/:id/edit、/posts/:id/show)。对于非 CRUD 页面,请使用 <CustomRoutes>。使用 useCreatePath() 构建资源 URL,并使用 react-admin 的 <Link> 进行导航。默认路由器是 react-router(HashRouter),但也可通过 routerProvider 使用 TanStack Router。请参阅 Routing。

数据获取

查询 Hook(读取数据)
const { data, total, isPending, error } = useGetList('posts', {
    pagination: { page: 1, perPage: 25 },
    sort: { field: 'created_at', order: 'DESC' },
    filter: { status: 'published' },
});

const { data: record, isPending } = useGetOne('posts', { id: 123 });
const { data: records } = useGetMany('posts', { ids: [1, 2, 3] });
const { data, total } = useGetManyReference('comments', {
    target: 'post_id', id: 123,
    pagination: { page: 1, perPage: 25 },
});

请参阅 useGetList、useGetOne。

变更钩子(写入数据)

所有变更都会返回 [mutate, state]。它们支持三种变更模式:

  • pessimistic(默认):等待服务端响应,然后更新 UI。
  • optimistic:立即更新 UI,服务端出错时恢复。
  • undoable:更新 UI,显示撤销通知,并在延迟后提交。
const [create, { isPending }] = useCreate();
const [update] = useUpdate();
const [deleteOne] = useDelete();

// Call with resource and params
create('posts', { data: { title: 'Hello' } });
update('posts', { id: 1, data: { title: 'Updated' }, previousData: record });
deleteOne('posts', { id: 1, previousData: record });

传入 mutationMode: 'optimistic' 或 'undoable',即可立即获得 UI 反馈。参见 useCreate、useUpdate。

身份验证与授权

const authProvider = {
    login: ({ username, password }) => Promise<void>,
    logout: () => Promise<void>,
    checkAuth: () => Promise<void>,           // Verify credentials are valid
    checkError: (error) => Promise<void>,      // Detect auth errors from API responses
    getIdentity: () => Promise<{ id, fullName, avatar }>,
    getPermissions: () => Promise<any>,
    canAccess: ({ resource, action, record }) => Promise<boolean>,  // RBAC
};

每个 auth provider 方法都有对应的钩子(例如 useGetIdentity()、useCanAccess())。

  • 自定义路由默认是公开的。 使用 <Authenticated> 包裹,或调用 useAuthenticated() 来要求用户登录。参见 Authenticated。
  • 在 authProvider.canAccess() 中集中处理授权,而不是在各个组件中处理。使用 useCanAccess() 检查权限。参见 useCanAccess 和 AuthRBAC。
  • dataProvider 必须在请求中携带凭据(Bearer 令牌、Cookie)——authProvider 负责登录,而 dataProvider 负责 API 调用。在 data provider 配置中设置 httpClient。

实体之间的关系

为某个页面预先获取所有数据(包括关联数据)是一种反模式。应改为使用引用字段和输入控件,按需获取关联记录。

显示关联记录(字段)
{/* Show a the company of the current record based on its company_id */}
<ReferenceField source="company_id" reference="companies" />

{/* Show a list of related records (reverse FK) */}
<ReferenceManyField reference="comments" target="post_id">
    <DataTable>
        <TextField source="body" />
        <DateField source="created_at" />
    </DataTable>
</ReferenceManyField>

{/* Show multiple referenced records (array of IDs) */}
<ReferenceArrayField source="tag_ids" reference="tags">
    <SingleFieldList>
        <ChipField source="name" />
    </SingleFieldList>
</ReferenceArrayField>

参见 ReferenceField、ReferenceManyField、ReferenceArrayField。

编辑关联记录(输入控件)
{/* Select from another resource (FK) */}
<ReferenceInput source="company_id" reference="companies" />

{/* Multi-select from another resource (array of IDs) */}
<ReferenceArrayInput source="tag_ids" reference="tags" />

参见 ReferenceInput、ReferenceArrayInput。

表单

React-admin 表单基于 react-hook-form 构建。单列布局使用 <SimpleForm>,多标签页布局使用 <TabbedForm>。参见 SimpleForm、TabbedForm。

向输入组件传入验证器:required()、minLength(min)、maxLength(max)、minValue(min)、maxValue(max)、number()、email()、regex(pattern, message),或返回错误字符串的自定义函数。

<TextInput source="title" validate={[required(), minLength(3)]} />

使用 RHF 的 useWatch() 创建可根据字段值动态响应的表单:

资源定义

将资源组件封装在索引文件中,以便简洁地导入:

// posts/index.ts
export default {
    list: PostList,
    create: PostCreate,
    edit: PostEdit,
    icon: PostIcon,
    recordRepresentation: (record) => record.title,  // How records appear in references
};

参见 Resource、RecordRepresentation。

自定义数据提供者方法

使用特定领域的方法扩展 dataProvider:

const dataProvider = {
    ...baseDataProvider,
    archivePost: async (id) => { /* custom logic */ },
};
// Call via useDataProvider and useQuery:
// const dp = useDataProvider(); 
// const { data } = useQuery(['archivePost', id], () => dp.archivePost(id));

持久化客户端状态(Store)

使用 useStore() 保存持久化的用户偏好(主题、列可见性、已保存的筛选条件):

const [theme, setTheme] = useStore('theme', 'light');

参见 Store。

通知、重定向、刷新

const notify = useNotify();
const redirect = useRedirect();
const refresh = useRefresh();

notify('Record saved', { type: 'success' });
redirect('list', 'posts');        // Navigate to /posts
redirect('edit', 'posts', 123);   // Navigate to /posts/123
refresh();                         // Invalidate all queries

弃用项

  • 使用 DataTable 替代 Datagrid
  • 授权检查优先使用 <CanAccess> 和 useCanAccess

评论

支持 Markdown