RecursionField
描述
递归渲染组件,主要基于 JSON Schema 文档 做递归渲染,它是 SchemaField 组件内部的核心渲染组件,当然,它是可以独立于 SchemaField 单独使用的,我们使用的时候主要是在自定义组件中使用,用于实现具有递归渲染能力的自定义组件
Schema 协议说明
本页只说明 @silver-formily/react 如何递归消费 Schema。Schema、ISchema、属性协议和联动协议的完整定义请查看 JSON Schema 文档。
简易递归
可以从组件属性中读取独立的 schema 对象,传给 RecursionField 渲染。自定义组件中配合 onlyRenderProperties 可以只渲染 schema 的 properties 部分。
import type { ISchema } from '@silver-formily/json-schema'
import { createForm } from '@silver-formily/core'
import { createSchemaField, FormProvider, RecursionField } from '@silver-formily/react'
import { Input } from 'antd'
const form = createForm()
// 自定义组件内用 RecursionField 只渲染 schema 的 properties 部分
function Custom(props: { name?: string, schema?: ISchema }) {
return (
<RecursionField
name={props.name}
schema={props.schema}
onlyRenderProperties
/>
)
}
const SchemaField = createSchemaField({
components: {
Custom,
Input,
},
})
export default function Demo() {
return (
<FormProvider form={form}>
<SchemaField>
<SchemaField.Object
name="custom"
x-component="Custom"
x-component-props={{
schema: {
type: 'object',
properties: {
input: {
'type': 'string',
'x-component': 'Input',
},
},
},
}}
/>
</SchemaField>
</FormProvider>
)
}import type { ISchema } from '@silver-formily/json-schema'
import { createForm } from '@silver-formily/core'
import { createSchemaField, FormProvider, RecursionField } from '@silver-formily/react'
import { Input } from 'antd'
const form = createForm()
// 自定义组件内用 RecursionField 只渲染 schema 的 properties 部分
function Custom(props: { name?: string, schema?: ISchema }) {
return (
<RecursionField
name={props.name}
schema={props.schema}
onlyRenderProperties
/>
)
}
const SchemaField = createSchemaField({
components: {
Custom,
Input,
},
})
export default function Demo() {
return (
<FormProvider form={form}>
<SchemaField>
<SchemaField.Object
name="custom"
x-component="Custom"
x-component-props={{
schema: {
type: 'object',
properties: {
input: {
'type': 'string',
'x-component': 'Input',
},
},
},
}}
/>
</SchemaField>
</FormProvider>
)
}
自增列表递归
使用useField和useFieldSchema来获取当前字段上下文中的字段实例和字段 schema
import type { ArrayField as ArrayFieldType } from '@silver-formily/core'
import type { Schema } from '@silver-formily/json-schema'
import { createForm, isArrayField } from '@silver-formily/core'
import {
createSchemaField,
FormProvider,
observer,
RecursionField,
useField,
useFieldSchema,
} from '@silver-formily/react'
import { Button, Input, Space } from 'antd'
const form = createForm()
// 自增列表组件:读取当前字段实例与 schema,用 RecursionField 递归渲染 items
const ArrayItems = observer(() => {
const field = useField<ArrayFieldType>()
const schema = useFieldSchema()
function handleAdd() {
if (isArrayField(field)) {
field.push({ id: Date.now() })
}
}
const itemSchema = schema?.items
const normalizedItemSchema = (Array.isArray(itemSchema) ? itemSchema[0] : itemSchema) as Schema
const items = isArrayField(field) && Array.isArray(field.value)
? field.value.map((item, index) => (
<div key={item.id ?? index} style={{ marginBottom: 10 }}>
<Space>
<RecursionField schema={normalizedItemSchema} name={index} />
<Button onClick={() => field.remove(index)}>
Remove
</Button>
</Space>
</div>
))
: null
return (
<div>
{items}
<Button onClick={handleAdd}>Add</Button>
</div>
)
})
const SchemaField = createSchemaField({
components: {
ArrayItems,
Input,
},
})
export default function Demo() {
return (
<FormProvider form={form}>
<SchemaField>
<SchemaField.Array name="custom" x-component="ArrayItems">
<SchemaField.Object>
<SchemaField.String name="input" x-component="Input" />
</SchemaField.Object>
</SchemaField.Array>
</SchemaField>
</FormProvider>
)
}import type { ArrayField as ArrayFieldType } from '@silver-formily/core'
import type { Schema } from '@silver-formily/json-schema'
import { createForm, isArrayField } from '@silver-formily/core'
import {
createSchemaField,
FormProvider,
observer,
RecursionField,
useField,
useFieldSchema,
} from '@silver-formily/react'
import { Button, Input, Space } from 'antd'
const form = createForm()
// 自增列表组件:读取当前字段实例与 schema,用 RecursionField 递归渲染 items
const ArrayItems = observer(() => {
const field = useField<ArrayFieldType>()
const schema = useFieldSchema()
function handleAdd() {
if (isArrayField(field)) {
field.push({ id: Date.now() })
}
}
const itemSchema = schema?.items
const normalizedItemSchema = (Array.isArray(itemSchema) ? itemSchema[0] : itemSchema) as Schema
const items = isArrayField(field) && Array.isArray(field.value)
? field.value.map((item, index) => (
<div key={item.id ?? index} style={{ marginBottom: 10 }}>
<Space>
<RecursionField schema={normalizedItemSchema} name={index} />
<Button onClick={() => field.remove(index)}>
Remove
</Button>
</Space>
</div>
))
: null
return (
<div>
{items}
<Button onClick={handleAdd}>Add</Button>
</div>
)
})
const SchemaField = createSchemaField({
components: {
ArrayItems,
Input,
},
})
export default function Demo() {
return (
<FormProvider form={form}>
<SchemaField>
<SchemaField.Array name="custom" x-component="ArrayItems">
<SchemaField.Object>
<SchemaField.String name="input" x-component="Input" />
</SchemaField.Object>
</SchemaField.Array>
</SchemaField>
</FormProvider>
)
}
API
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| schema | 要渲染的 Schema 对象 | ISchema | — |
| name | 渲染时挂载的字段名称,常配合 basePath 推导路径 | SchemaKey (string | number) | — |
| basePath | 计算 name 的基路径 | FormPathPattern | 当前字段路径 |
| propsRecursion | 递归渲染子节点时是否透传 mapProperties / filterProperties | boolean | false |
| onlyRenderProperties | 仅渲染子节点 properties,不渲染当前节点 | boolean | false |
| onlyRenderSelf | 仅渲染当前节点,不自动递归子节点 | boolean | false |
| mapProperties | 属性映射函数,可在渲染前改写 schema | Function | — |
| filterProperties | 属性过滤函数,返回 false 的节点不会渲染 | Function | — |
FormPathPattern
import type { Pattern as FormPathPattern } from '@silver-formily/path'schema 必填,其他属性可选。省略 name 时仅渲染 properties,不会默认取 schema.name。具名 object/void 节点会渲染自身及 properties,除非开启 onlyRenderSelf。onlyRenderProperties 跳过具名 object/void 包装,但不会把 array 变成 items 渲染器:数组项由数组组件负责渲染。无 name 且开启 onlyRenderSelf 时不渲染内容。
basePath 默认取父字段 address,开启 onlyRenderProperties 时还会拼接当前 name。先执行映射再过滤,仅当 propsRecursion 为 true 时向后代传递映射和过滤函数。声明中的 children?: React.ReactNode 当前未被 RecursionField 消费,请通过 schema 提供内容。独立使用时,创建字段仍需表单上下文,注册名称解析仍需组件映射上下文。