react-admin<TreeInput>树形节点选择组件完整指南:从树数据到外键选择
【免费下载链接】react-adminA frontend Framework for single-page applications on top of REST/GraphQL APIs, using TypeScript, React and Material Design项目地址: https://gitcode.com/gh_mirrors/re/react-admin
<TreeInput>是 react-admin 生态中(@react-admin/ra-tree,Enterprise Edition 组件)专用于在表单中从一个树形结构里选择一个或多个节点的输入组件,适合为"产品选择分类"这类层级数据建模场景。阅读本文后,你将掌握树数据结构(TreeRecord)的约定、<TreeInput>全部核心 Props 的含义与默认值、如何通过dataProvider.getTree()/useGetTree拉取选项,以及如何配合<ReferenceNodeInput>声明式地完成多对一/一对一外键选择。
什么是<TreeInput>,在什么场景下使用它
<TreeInput>的作用是在 react-admin 表单中渲染一棵可勾选/可点选的树,让用户从树形选项中选择一个或多个节点。官方文档对其定位是:用于选择"树节点"(Tree node)数据类型的输入组件——在 docs/Inputs.md 的"按数据类型选择输入组件"对照表中,数据类型Tree node(示例值42)对应的输入组件正是<TreeInput>。
典型场景包括:
- 给商品选择一个所属分类(分类本身是一棵多级树:服装 → 男装 → 西服 → 西裤/夹克);
- 为组织架构、权限菜单、地区编码等层级数据指定父级或归属节点;
- 在树形参考资源(reference resource)上设置外键字段。
与普通的<SelectInput>、<AutocompleteInput>不同,<TreeInput>面向的是具有父子层级关系的数据,用户可以在展开/折叠的树中直观地完成选择,而不是在扁平的选项列表中查找。
安装与引入
<TreeInput>由ra-tree模块提供(react-admin 官方 Enterprise Edition 功能),引入方式如下:
import { TreeInput } from '@react-admin/ra-tree'; import { SimpleForm } from 'react-admin';在文章所依托的仓库中,<TreeInput>与<Tree>、<ReferenceNodeInput>、<TreeWithDetails>等组件一起出现在 docs/Reference.md 的参考资源列表中,docs/Features.md 也将其归类为"用于树形结构的输入组件"。ra-tree同时提供dataProvider树方法扩展与配套 Hooks(详见 docs/useGetTree.md)。
树数据结构(TreeRecord)
<TreeInput>的dataprop 接收的是dataProvider.getTree()返回格式的数组:由若干节点对象组成的数组,每个节点含id、children字段,其中children是子节点 id 组成的数组(可嵌套形成多级树)。
一份典型的树数据(来自官方文档示例)如下:
const data = [ { id: 1, title: 'Clothing', isRoot: true, children: [2, 6] }, { id: 2, title: 'Men', children: [3] }, { id: 3, title: 'Suits', children: [4, 5] }, { id: 4, title: 'Slacks', children: [] }, { id: 5, title: 'Jackets', children: [] }, { id: 6, title: 'Women', children: [7, 10, 11] }, { id: 7, title: 'Dresses', children: [8, 9] }, { id: 8, title: 'Evening Gowns', children: [] }, { id: 9, title: 'Sun Dresses', children: [] }, { id: 10, title: 'Skirts', children: [] }, { id: 11, title: 'Blouses', children: [] }, ];这种"平铺数组 +children存子节点 id"的结构称为TreeRecord。相关文档 docs/useGetTree.md 对这种格式有进一步说明:一个TreeRecord至少包含id字段和children字段(子节点 id 数组),例如[{ id: 1, title: 'foo1', children: [3, 4] }, ...]。isRoot字段用于标识根节点(<TreeInput>的hideRootNodes会用到它,见下文)。节点标题默认读取title字段,可通过titleField修改。
基础用法:在表单中渲染一棵可选择的树
将<TreeInput>放入 react-admin 表单组件(如<SimpleForm>)即可:
import { TreeInput } from '@react-admin/ra-tree'; import { SimpleForm } from 'react-admin'; export const SimpleTreeForm = () => ( <SimpleForm> <TreeInput source="category" data={[ { id: 1, title: 'Clothing', isRoot: true, children: [2, 6] }, { id: 2, title: 'Men', children: [3] }, { id: 3, title: 'Suits', children: [4, 5] }, { id: 4, title: 'Slacks', children: [] }, { id: 5, title: 'Jackets', children: [] }, { id: 6, title: 'Women', children: [7, 10, 11] }, { id: 7, title: 'Dresses', children: [8, 9] }, { id: 8, title: 'Evening Gowns', children: [] }, { id: 9, title: 'Sun Dresses', children: [] }, { id: 10, title: 'Skirts', children: [] }, { id: 11, title: 'Blouses', children: [] }, ]} /> </SimpleForm> );用户选中节点后,选中的节点 id 会被写入source指定的字段(单选时值为一个 id;multiple多选时值为 id 数组)。选中值如何与表单状态交互,遵循 react-admin 输入组件通用的source/parse/format机制(见下文"通用输入属性")。
Tip:如果希望自动从参考资源(reference resource)拉取树数据,请将
<TreeInput>放进<ReferenceNodeInput>中使用(见"选择外键"一节),无需手动传data。
Props 完整参考
<TreeInput>的专属 props 如下表(官方文档原始表格,字段说明已补充取值范围):
| Prop | 必填 | 类型 | 默认值 | 说明 |
|---|---|---|---|---|
source | 必填 | string | - | 表单中该输入对应记录的字段名;当用于<ReferenceNodeInput>内部时可不填 |
checkStrictly | 可选 | boolean | true | 严格勾选模式,父节点与子节点的勾选状态互不关联 |
data | 可选 | 对象数组 | - | 树数据,格式为dataProvider.getTree()的返回值(含children字段的节点数组) |
id | 可选 | string | - | 输入框的 HTML id |
hideRootNodes | 可选 | boolean | false | 设为true隐藏所有根节点 |
margin | 可选 | string | dense | 输入框外边距,可取值none、dense、normal(对应 MUI Input margins) |
multiple | 可选 | boolean | false | 设为true允许多选,此时每行渲染一个复选框 |
size | 可选 | string | small | 输入框尺寸,可取值small、medium、large(对应 MUI Input sizes) |
titleField | 可选 | string | 'title' | 存放节点标题的字段名 |
variant | 可选 | string | filled | 外观变体,可取值standard、outlined、filled(对应 MUI Input variants) |
除此之外,<TreeInput>还接受:
- react-admin 的通用输入属性(common input props);
- 底层 rc-tree
<Tree>组件的全部 props(<TreeInput>内部正是基于 rc-tree 的<Tree>实现的)。
下面逐个讲解核心 props。
source与通用输入属性
source指定表单记录中承载所选节点 id 的字段名,是唯一必填 prop;只有当<TreeInput>被包在<ReferenceNodeInput>内部(由外层控制器提供上下文)时才可省略。
作为 react-admin 标准输入组件,<TreeInput>同样接受 docs/Inputs.md 中列出的通用输入属性:className、defaultValue、readOnly、disabled、format、fullWidth、helperText、label、parse、sx、validate等。例如可以用validate做必填校验、用label={false}隐藏标签、用sx定制样式。react-admin 使用 react-hook-form 控制表单输入,因此<TreeInput>也接受useController的所有选项;未被消费的 props 会继续向下透传给底层的 Material UI / rc-tree 组件。
data:树数据的两种来源
data是<TreeInput>的选项数据源,必须是以children字段关联的节点数组(格式见上文"树数据结构")。
<TreeInput source="category" data={[ { id: 1, title: 'Clothing', isRoot: true, children: [2, 6] }, { id: 2, title: 'Men', children: [3] }, { id: 3, title: 'Suits', children: [4, 5] }, { id: 4, title: 'Slacks', children: [] }, { id: 5, title: 'Jackets', children: [] }, { id: 6, title: 'Women', children: [7, 10, 11] }, { id: 7, title: 'Dresses', children: [8, 9] }, { id: 8, title: 'Evening Gowns', children: [] }, { id: 9, title: 'Sun Dresses', children: [] }, { id: 10, title: 'Skirts', children: [] }, { id: 11, title: 'Blouses', children: [] }, ]} />直接写死的data适合静态数据。如果你正在编辑的是一个关联关系,更推荐用<ReferenceNodeInput>在组件挂载时从参考资源拉取data,从而省去手动获取数据的样板代码(详见"选择外键"一节)。
checkStrictly:父子节点勾选是否联动
默认情况下,<TreeInput>使用 rc-tree<Tree>的checkStrictly特性,允许叶子节点与父节点相互独立地勾选(勾选父节点不会自动带上全部子节点,反之亦然)。文档给出的默认值为true。
如果你希望遵循"勾选父节点即全选子节点"的级联逻辑,可显式关闭:
<TreeInput source="category" data={treeData} multiple checkStrictly={false} />需要说明的是:checkStrictly只在multiple多选(带复选框)模式下才有实际意义;单选模式下不存在父子级联问题。
multiple:多选模式
默认单选。设置multiple后,树中每行会渲染一个复选框,用户可以同时勾选多个节点,表单值变为 id 数组:
import { SimpleForm } from 'react-admin'; import { TreeInput } from '@react-admin/ra-tree'; import treeData from './treeData'; export const SimpleTreeForm = () => ( <SimpleForm> <TreeInput source="category" data={treeData} multiple /> </SimpleForm> );多选模式下树数据量较大时,可搭配checkStrictly决定是否允许父子节点分别勾选,从而精确控制用户的可选粒度。
hideRootNodes:隐藏根节点
当树数据中的根节点带isRoot: true标记(或由ra-tree数据提供者约定)时,设置hideRootNodes会隐藏所有根节点,只展示其子孙节点,适合"根节点只是一个容器、不应被选中"的业务模型:
<TreeInput source="category" data={treeData} hideRootNodes />titleField:指定节点标题字段
节点在树中显示的文字默认取自title字段。如果树记录用其他字段存放标题(例如name),通过titleField指定即可:
<TreeInput source="category" data={treeData} titleField="name" />margin/size/variant:MUI 外观定制
这三个 props 直接映射 Material UI 输入框的外观体系:
margin:默认dense,可选none/dense/normal,控制输入框外边距(MUI Input margins);size:默认small,可选small/medium/large,控制输入框尺寸(MUI Input sizes);variant:默认filled,可选standard/outlined/filled,控制输入框边框样式(MUI Input variants)。
由此可以推断,<TreeInput>外壳是一个 MUI 风格的可展开下拉容器,内嵌 rc-tree 渲染的树。这与 react-admin"每个输入组件内部通常是 MUI 组件、多余 props 透传"的设计一致(见 docs/Inputs.md)。
获取选项:dataProvider.getTree()与useGetTree
实际项目中树数据通常来自后端 API。ra-tree扩展了 dataProvider,新增了getTree(resource)方法(docs/useGetTree.md 列出了ra-tree推荐的整套树方法:读取类的getTree、getRootNodes、getParentNode、getChildNodes,写入类的moveAsNthChildOf、moveAsNthSiblingOf、addRootNode、addChildNode、deleteBranch)。
手动获取树数据的标准做法是使用useGetTreeHook,它会在组件挂载时调用dataProvider.getTree(),返回TreeRecord数组:
import { useGetTree, TreeInput } from '@react-admin/ra-tree'; const CategoryInput = () => { const { isLoading, data: tree } = useGetTree('categories'); if (isLoading) return <Loading />; return ( <TreeInput source="category" data={tree} /> ); };isLoading用于在数据加载期间显示加载指示器。useGetTree返回的data是TreeRecord平铺数组(至少含id与children字段),正好就是<TreeInput>的data所需格式。
不过,官方文档明确指出:如果是为了给另一个资源设置外键而填充<TreeInput>的选项,大多数情况下应该用<ReferenceNodeInput>而非手动useGetTree——前者把"拉取参考资源树"这件事声明式地封装好了。
选择外键:配合<ReferenceNodeInput>
当使用<TreeInput>为一个多对一(many-to-one)或一对一(one-to-one)关系设置外键时,手动获取选项(上一节的useGetTree方案)固然可行,但 react-admin 为此提供了声明式的捷径——<ReferenceNodeInput>:
import { Edit, SimpleForm, TextInput } from 'react-admin'; import { ReferenceNodeInput, TreeInput } from '@react-admin/ra-tree'; const ProductEdit = () => ( <Edit> <SimpleForm> <TextInput source="name" /> <ReferenceNodeInput source="category_id" reference="categories"> <TreeInput /> </ReferenceNodeInput> </SimpleForm> </Edit> );<ReferenceNodeInput>是一个控制器组件:挂载时从reference指定的资源调用dataProvider.getTree()拉取整棵树,创建"树选项上下文",然后渲染子组件。默认子组件就是一个不带 props 的<TreeInput>;当你需要自定义<TreeInput>的 props(例如multiple)时,显式传入子元素即可:
<ReferenceNodeInput source="category_id" reference="categories"> <TreeInput multiple checkStrictly={false} /> </ReferenceNodeInput><ReferenceNodeInput>还支持metaprop,用于在调用getTree()时向 dataProvider 传递元数据:
<ReferenceNodeInput source="category_id" reference="categories" meta={{ foo: 'bar' }} />在 docs/Inputs.md 的类型对照表中,<ReferenceNodeInput>被归类为处理"关联记录(Related records)"的输入组件之一,与<ReferenceManyInput>、<ReferenceManyToManyInput>、<ReferenceOneInput>并列;而 docs/ReferenceInput.md 也建议:当参考资源本身是一棵树时,用<ReferenceNodeInput>替代<ReferenceInput>。这与<TreeInput>的设计意图完全一致——树形外键是它最核心的实战场景。
底层实现:rc-tree 之上的表单化封装
从文档与仓库可以梳理出<TreeInput>的分层实现结构:
- 渲染层:
<TreeInput>内部使用 rc-tree 的<Tree>组件(该库是 React 生态中广泛使用的树组件),因此它"接受 rc-tree<Tree>的所有 props"——defaultExpandedKeys、expandedKeys、defaultSelectedKeys、selectedKeys、onCheck、onSelect、onExpand、onDrop、motion、sx等均可按需透传(这些 props 的语义可在 docs/Tree.md 的<Tree>组件 props 表中对照查看); - 表单层:
<TreeInput>按 react-admin 输入组件约定接入 react-hook-form,通过source读写表单值,接受通用输入属性与useController选项,因此可以无缝嵌入<SimpleForm>、<Edit>等表单结构; - 数据层:选项来自
data(getTree()格式的TreeRecord[]),既可以直接传字面量,也可以用useGetTree拉取,或者由<ReferenceNodeInput>作为控制器提供。
小结
<TreeInput>是 react-admin 中处理层级/树形数据选择的专用输入组件,核心要点可概括为:
- 数据契约:
data必须使用dataProvider.getTree()返回的TreeRecord格式(平铺数组 +children子节点 id 数组); - 核心 Props:
source必填;multiple开启多选复选框;checkStrictly(默认true)控制父子节点是否独立勾选;hideRootNodes隐藏根节点;titleField指定标题字段;margin/size/variant对齐 MUI 外观体系; - 选项获取:静态数据直接传
data;动态数据用useGetTree;设置外键则优先用<ReferenceNodeInput>声明式完成; - 可扩展性:底层基于 rc-tree,全部 rc-tree props 均可透传,配合通用输入属性可以满足大部分表单场景。
相关延伸阅读:<ReferenceNodeInput>(树形外键输入)、useGetTree(树数据 Hook 与 dataProvider 树方法)、<Tree>(纯展示型树组件)、<TreeWithDetails>(树 + 详情布局)。
【免费下载链接】react-adminA frontend Framework for single-page applications on top of REST/GraphQL APIs, using TypeScript, React and Material Design项目地址: https://gitcode.com/gh_mirrors/re/react-admin
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考