问题背景
公司的UI库集成formily 状态管理后,迅速投入了开发和应用,虽然我们的表单组件都配置了预览态,但仍不能满足需求,有些时候还是需要给默认的预览态再加一些样式;
举例说明,比如我们的input组件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import React from 'react';
import { connect, mapProps, mapReadPretty } from '@formily/react';
import Input from '../../Input';
import { InputProps } from '../../Input/input';
import PreviewText from '../PreviewText/index';
const FormInput = connect(
Input,
mapProps((props: InputProps) => {
return {
...props,
};
}),
mapReadPretty(PreviewText.Input) // 阅读态样式对应了PreviewText.Input中的样式
);
export default FormInput;
|
在previewText组件中的预览:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
const PlaceholderContext = createContext<React.ReactNode>('--');
const Placeholder = PlaceholderContext.Provider;
const usePlaceholder = (value?: any) => {
const placeholder = useContext(PlaceholderContext) || '--';
return isValid(value) && value !== '' ? value : placeholder;
};
// Input 对应的阅读态 这里的样式取决于代码里className中的样式
const Input: React.FC<InputProps> = (props) => { // props Input 组件属性
return (
<div className={classNames(styles.previewContainer, sdf.bodyPrimary)}>
{usePlaceholder(props.value)}
</div>
);
};
PreviewText.Input = Input;
export default PreviewText;
|
在项目中的应用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
import { FormInput, FormItem} from 'sugar-design' // sugar-design 是我司自研发的UI库,类似于antd
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'
class Demo extends React.PureComponent {
render() {
const SchemaField = createSchemaField({
components: {
FormInput,
FormItem,
},
});
const form = createForm();
return (
<FormProvider form={form}>
<SchemaField>
<SchemaField.String
name="input"
title="输入框"
default={'input'}
required
x-decorator="FormItem"
x-decorator-props = {{
spacing: "md",
}}
x-component="FormInput"
/>
</SchemaField>
<br/>
<Button
onClick={() => {
form.setState((state) => {
state.readPretty = !state.readPretty
})
}}
>
切换阅读态
</Button>
</FormProvider>
)
}
}
|
方案探讨
在官方的文档中,mapReadPretty 的签名是:
1
2
3
|
interface mapReadPretty<Target extends React.FC> {
(component: Target, readPrettyProps?: React.ComponentProps<Target>): React.FC
}
|
可以看到,connect时候调用mapReadPretty 方法时,第一个参数是自定义的预览组件,第二个是该组件的参数,在我们使用connect 之后的formInput 的时候,自定义预览组件在内部自动被使用了,没有一个入口可以传入我们想要的样式,但是在自定义预览组件被调用的时候,我们可以看到formily将组件属性都传入了,因此我想到了第一个方案: 通过给Input组件添加自定义样式参数previewStyle
准备添加的时候发现,这样会给input 组件添加属性,而这个属性实际在input组件中并没有用到,而是在预览状态下用到,预览态其实是formily中给到的概念,切换某个表单项状态,input组件只是其编辑态需要调用的一个组件,况且其他组件如果要添加自定义预览样式,也要新增属性的话,对我们的组件库侵入比较大,这样一想,给input增加组件并不妥;
也有小伙伴提出,可以像提供默认placeholder一样来提供一个自定义样式的provider:
1
2
3
4
5
6
7
8
9
10
11
12
|
// PreviewText 中增加默认placeholder
const PlaceholderContext = createContext<React.ReactNode>('--');
const Placeholder = PlaceholderContext.Provider;
Text.Placeholder = Placeholder;
const PreviewText = Text;
// 使用:
<PreviewText.Placeholder value="暂无数据">
<FormProvider form={form}>
<SchemaField>
...
|
如果要加一个类似
1
|
<PreviewText.previewStyle value={{color: 'red'}}>
|
这样的功能也可以,但是这样做就会给代码加一层组件包裹,而且这样添加的样式无法实现个别组件定制,也就是provider包裹的只能提供一种样式给到所有组件的预览态,不够灵活
于是只有利用Schema中的自定义属性 x-data 了,缺点是增加自定义属性一定要加好文档,否则在官方文档里看不到的开发者也不能很好的理解和应用;
优点是x-data中的属性是被formily 监控到的,可以实现异步更新,属性变化,页面更新,当然也支持表单项维度的配置样式。
开发阶段
只增加style也不够灵活,开发者也有用类名传递的需求,于是一起增加了两个属性:previewStyle
,previewClassName
,previewText 中的部分实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// 阅读态 支持schema中的data.previewStyle自定义阅读样式
const PreviewRender: React.FC<previewRenderProps> = observer((props: previewRenderProps) => {
const field = useField<Field>();
const previewStyle: CSSProperties = field.data?.previewStyle;
const previewClassName: string = field.data?.previewClassName;
return (
<div
className={classNames(styles.previewContainer, sdf.bodyPrimary, styles[useSize(props.size)], previewClassName)}
style={previewStyle}
>
{usePlaceholder(props.previewText)}
</div>
);
});
// Input 对应的阅读态
const Input: React.FC<InputProps> = (props) => {
return <PreviewRender previewText={usePlaceholder(props.value)} size={props.size} />;
};
|
这样就实现了配置表单schema中 x-data 属性的previewStyle
,previewClassName
,可以自定义预览态样式的问题了。