RecursionField
Description
A recursive rendering component, mainly performing recursive rendering based on the JSON Schema documentation. It is the core rendering component inside the SchemaField component. Of course, it can also be used independently of SchemaField; we mainly use it inside custom components to implement custom components with recursive rendering capabilities.
Schema Protocol Notes
This page only explains how @silver-formily/react recursively consumes Schemas. For the complete definitions of Schema, ISchema, the property protocol and the linkage protocol, please refer to the JSON Schema documentation.
Simple Recursion
You can read a standalone schema object from the component attributes and pass it to RecursionField for rendering. Inside a custom component, onlyRenderProperties renders only the properties of the schema.
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>
)
}
Self-incrementing List Recursion
Use useField and useFieldSchema to obtain the field instance and the field schema from the current field context.
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
| Attribute | Description | Type | Default |
|---|---|---|---|
| schema | The Schema object to render | ISchema | — |
| name | Field name used when mounting during rendering, often combined with basePath to resolve the path | SchemaKey (string | number) | — |
| basePath | Base path for resolving name | FormPathPattern | current field path |
| propsRecursion | Whether to pass mapProperties / filterProperties through when recursively rendering child nodes | boolean | false |
| onlyRenderProperties | Only render the child nodes' properties, without rendering the current node | boolean | false |
| onlyRenderSelf | Only render the current node, without automatically recursing into child nodes | boolean | false |
| mapProperties | Property mapping function, can rewrite the schema before rendering | Function | — |
| filterProperties | Property filter function; nodes for which it returns false are not rendered | Function | — |
FormPathPattern
import type { Pattern as FormPathPattern } from '@silver-formily/path'schema is required; other props are optional. If name is omitted, only properties are rendered; it does not default to schema.name. Named object/void nodes render their properties unless onlyRenderSelf is true. onlyRenderProperties skips named object/void wrappers, but does not turn an array into an items renderer: array items must be rendered by the array component. onlyRenderSelf with no name renders nothing.
basePath defaults to the parent field address; with onlyRenderProperties, the current name is appended. Mapping runs before filtering, and recursive propagation is enabled only by propsRecursion. The declared children?: React.ReactNode is currently not consumed by RecursionField; supply content through the schema. Direct usage still requires form context when creating fields and a component registry when using registered names.