扩展一个通用组件
- github: github.com/easy-page/e…
- 文档站官网:easy-page.github.io/easy-page-d…
- 文档站 github: github.com/easy-page/e…
我们知道,我们有个默认的 UI 库,包含了一些基础组件,但若是这些组件还无法满足需求,我们希望通过:ui
属性指定我们想要的组件,该怎么办呢?
- 我们可以扩展组件
扩展一个组件,大概有这些步骤:
- 需要通过
connector
给组件连接副作用处理。 - 需要声明组件的:
Props
类型,用于属性透传Props
。Props
的组成为:框架通用 Props
+组件自身 Props
+扩展 Props
- 定义组件的
EffectType
,使其具备副作用处理能力
听起来很复杂,实际应该比较简单,我们可以看看下面的例子。
扩展一个输入框
我们自定义一个输入框吧:
// Input/index.tsx
import { ComponentProps, connector } from '@easy-page/antd-ui';
import { Input as AntdInput, InputProps as AntdInputProps } from 'antd';
import React from 'react';
/** 1. 组件的基础属性 **/
export type InputBaseProps = AntdInputProps;
/** 2. 结合框架的 Props */
export type CustomInputProps = ComponentProps<InputBaseProps, string>;
/** 3. 声明类型提示 */
declare module '@easy-page/antd-ui' {
export interface FieldUIConfig {
customInput?: InputBaseProps;
}
}
/** 4. 通过 connector 和 React.memo 包裹组件 */
export const CustomInput = connector(
React.memo((props: CustomInputProps) => {
const { frameworkProps: _, value, onChange, ...baseProps } = props;
return <AntdInput {...baseProps} onChange={onChange} value={value} />;
})
);
通过 declare module '@easy-page/antd-ui'
的声明,我们就可以定义字段时,有提示:
基于上述:1 - 4 步,我们就完成了一个组件的扩展,我们注册一下组件。首先,对 UI_COMPONETS
常量扩展一下:
import { UI_COMPONENTS } from "@easy-page/antd-ui";
export const EXT_UI_COMPONENTS = {
...UI_COMPONENTS,
CUSTOM_INPUT: 'customInput'
}
- 这里有个细节:
CUSTOM_INPUT: 'customInput'
后面的:customInput
其实是declare module '@easy-page/antd-ui'
中定义的属性的 key,否则找不到对应的配置。
在 EasyPage
中注册此组件:
import { DEFAULT_COMPONENTS, EasyPage } from '@easy-page/antd-ui';
import React from 'react';
import '../common/style';
import '@easy-page/antd-ui/style.css';
import { pageInfo } from './pageInfo';
import { EXT_UI_COMPONENTS } from './constant';
import { CustomInput } from './Input';
const Topic29 = () => {
return (
<EasyPage<{ name: string; age: string }>
{...pageInfo}
/** 按需加载表单所需组件 * */
components={{
...DEFAULT_COMPONENTS,
[EXT_UI_COMPONENTS.CUSTOM_INPUT]: CustomInput,
}}
pageType="form"
/>
);
};
export default Topic29;
接着,我们在定义字段的时候,指定我们的扩展组件,并给其透传属性:
import { nodeUtil } from '@easy-page/antd-ui';
import { EXT_UI_COMPONENTS } from './constant';
export const name = nodeUtil.createField(
'name',
'姓名',
{
value: '',
},
{
ui: EXT_UI_COMPONENTS.CUSTOM_INPUT,
customInput: {
placeholder: '这是姓名字段',
},
}
);
效果如下:
使用副作用
我们给定义的输入框增加一下副作用的能力:
- 可以基于副作用改变组件的
Props
首先,我们定义副作用类型:InputEffectedType
:
export type InputEffectedType = {
inputProps?: InputBaseProps;
};
/**
* - 1. 定义组件 Props,一般由:UI 库组件本身 Props + 框架通用组件 Props + 自定义组件 Props 构成
*/
export type InputProps = ComponentProps<InputBaseProps, any, InputEffectedType>;
然后,我们就拿着传递过来的副作用结果,直接放到组件上即可:
export const CustomInput = connector(
React.memo((props: CustomInputProps) => {
const {
frameworkProps: { effectedResult },
value,
onChange,
...baseProps
} = props;
console.log('baseProps:', baseProps);
return (
<AntdInput
{...baseProps}
{...(effectedResult?.inputProps || {})}
onChange={onChange}
value={value}
/>
);
})
);
如上,我们在:frameworkProps
中取出:effectedResult
放到了输入框上,就使其具备了此能力,效果如下:
- 姓名2 基于姓名的值展示:
placeholder
import { Empty, nodeUtil } from '@easy-page/antd-ui';
import { EXT_UI_COMPONENTS } from './constant';
import { InputEffectedType } from './Input';
export const name2 = nodeUtil.createField<
string,
{ name: string },
Empty,
InputEffectedType
>(
'name2',
'姓名2',
{
value: '',
actions: [
{
effectedKeys: ['name'],
initRun: true,
action: ({ effectedData }) => {
return {
effectResult: {
inputProps: {
placeholder: `基于 name: ${
effectedData.name || ''
} 的 placeholder`,
},
},
};
},
},
],
},
{
ui: EXT_UI_COMPONENTS.CUSTOM_INPUT,
}
);
效果如下:
扩展输入框的能力
我们给输入框增加一个能力:
-
当设置了:
trigger = 'onBlur'
时,在onBlur
时才触发值的变化。export type InputBaseProps = AntdInputProps & { /** 字段变化时间:onBlur 表示 onBlur 时才提交变化, 默认 onBlur */ trigger?: 'onChange' | 'onBlur'; };
详细改动如下:
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ComponentProps, connector } from '@easy-page/react-ui';
import { Input as AntdInput, InputProps as AntdInputProps } from 'antd';
import React, { useEffect, useState } from 'react';
export type InputBaseProps = AntdInputProps & {
/** 字段变化时间:onBlur 表示 onBlur 时才提交变化, 默认 onBlur */
trigger?: 'onChange' | 'onBlur';
};
export type InputEffectedType = {
inputProps?: InputBaseProps;
};
/**
* - 1. 定义组件 Props,一般由:UI 库组件本身 Props + 框架通用组件 Props + 自定义组件 Props 构成
*/
export type InputProps = ComponentProps<InputBaseProps, any, InputEffectedType>;
/**
* - 2. 重写 FieldUIConfig,增加组件配置提示
*/
declare module '@easy-page/react-ui/interface' {
export interface FieldUIConfig {
input?: InputBaseProps;
}
}
/**
* - 3. 编写通用组件逻辑
* @param props
* @returns
*/
export const Input = connector(
React.memo((props: InputProps) => {
const {
frameworkProps: { effectedResult },
value,
onChange,
onBlur,
trigger = 'onChange',
...baseProps
} = props;
const [fieldValue, setFieldValue] = useState(value);
useEffect(() => {
if (value && value !== fieldValue) {
setFieldValue(value);
}
}, [value]);
if (trigger === 'onChange') {
return (
<AntdInput
{...baseProps}
onBlur={onBlur}
{...(effectedResult?.inputProps || {})}
onChange={onChange}
value={value}
/>
);
}
return (
<AntdInput
{...baseProps}
{...(effectedResult?.inputProps || {})}
onBlur={(e) => {
onChange({ target: { value: fieldValue } });
onBlur?.(e);
}}
onChange={(val) => {
setFieldValue(val.target.value);
}}
value={fieldValue}
/>
);
})
);
- 其余组件扩展可参考:
@easy-page/antd-ui
里的组件进行扩展,流程大同小异。