背景
在我们使用formily开发的时候,不可避免会遇到定制组件的需求。 定制组件主要分为两种情况:
- 该功能底层组件支持,需要属性映射
- 该功能底层组件不支持,需要对底层组件进行定制开发
本篇文章介绍第一种情况,该功能底层组件支持,需要属性映射
Formily版本:V2
编码实战
schema中有一个自定义的属性withCount,input本身没有该属性,需要通过mapProps做映射处理
demo地址
demo-定开组件-属性映射
组件js
import React, { useMemo } from 'react';
import { createForm } from '@formily/core';
import { createSchemaField, FormProvider, connect, mapProps } from '@formily/react';
import { Form, Input, FormItem } from '@formily/antd';
import formData from './schema';
const { form: formprops, schema } = formData;
const CustomIput = connect(
Input,
mapProps((props: any, field) => {
window.console.log('CustomIput-props', props);
window.console.log('CustomIput-field', field);
const { withCount } = props;
return {
...props,
showCount: withCount,
};
}),
);
const SchemaField = createSchemaField({
components: {
Form,
FormItem,
Input,
CustomIput,
},
});
const FormRender = () => {
const form = useMemo(() => createForm({ validateFirst: true }), []);
return (
<FormProvider form={form}>
<Form {...formprops}>
<SchemaField schema={schema} />
</Form>
</FormProvider>
);
};
export default FormRender;
schema.js
const formData = {
form: {
labelCol: 6,
wrapperCol: 12,
},
schema: {
type: 'object',
properties: {
ke4g04rtzfe: {
type: 'string',
title: 'custom-Input',
'x-decorator': 'FormItem',
'x-component': 'CustomIput',
'x-validator': [],
'x-component-props': {
placeholder: '占位提示',
withCount: true,
maxLength: 20,
},
'x-decorator-props': {},
'x-designable-id': 'ke4g04rtzfe',
'x-index': 0,
description: '自定义的input组件',
},
l2w708hrhv6: {
type: 'string',
title: 'formily-Input',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-validator': [],
'x-component-props': {
placeholder: '占位提示',
withCount: true,
maxLength: 20,
},
'x-decorator-props': {},
'x-designable-id': 'l2w708hrhv6',
'x-index': 1,
description: 'formily封装的input组件',
},
},
'x-designable-id': 'sjo6h5jy439',
},
};
export default formData;
知识剖析
实现自定义组件,接入现成组件库的话,主要使用 connect/mapProps/mapReadPretty API
API源码
import React from 'react'
import { isFn, isStr, FormPath, each, isValid } from '@formily/shared'
import { isVoidField } from '@formily/core'
import { observer, Observer } from '@formily/reactive-react'
import { JSXComponent, IComponentMapper, IStateMapper } from '../types'
import { useField } from '../hooks'
import hoistNonReactStatics from 'hoist-non-react-statics'
export function mapProps<T extends JSXComponent>(
...args: IStateMapper<React.ComponentProps<T>>[]
) {
return (target: T) => {
return observer(
(props: any) => {
const field = useField()
const results = args.reduce(
(props, mapper) => {
if (isFn(mapper)) {
props = Object.assign(props, mapper(props, field))
} else {
each(mapper, (to, extract) => {
const extractValue = FormPath.getIn(field, extract)
const targetValue = isStr(to) ? to : (extract as any)
const originalValue = FormPath.getIn(props, targetValue)
if (extract === 'value') {
if (to !== extract) {
delete props.value
}
}
if (isValid(originalValue) && !isValid(extractValue)) return
FormPath.setIn(props, targetValue, extractValue)
})
}
return props
},
{ ...props }
)
return React.createElement(target, results)
},
{
forwardRef: true,
}
)
}
}
export function mapReadPretty<T extends JSXComponent, C extends JSXComponent>(
component: C,
readPrettyProps?: React.ComponentProps<C>
) {
return (target: T) => {
return observer(
(props) => {
const field = useField()
if (!isVoidField(field) && field?.pattern === 'readPretty') {
return React.createElement(component, {
...readPrettyProps,
...props,
})
}
return React.createElement(target, props)
},
{
forwardRef: true,
}
)
}
}
export function connect<T extends JSXComponent>(
target: T,
...args: IComponentMapper<T>[]
) {
const Target = args.reduce((target, mapper) => {
return mapper(target)
}, target)
const Destination = React.forwardRef(
(props: Partial<React.ComponentProps<T>>, ref) => {
return React.createElement(Target, { ...props, ref })
}
)
if (target) hoistNonReactStatics(Destination, target as any)
return Destination
}
export { observer, Observer }
参考资料
formily2.0-实现自定义组件 formily/react API-connect
|