用法
当我们项目在开发一类下拉框 Select 组件时,大致逻辑差不多。但是有几点不同:
- Select 的 Option 项渲染方式不同,有的是文本,有的是标签,有的是带图标的文本……
- Select 的 Option 数据获取方式不同,各类数据通过不同的 API 来获取。
- 由于下拉 Option 的数据不同,其筛选方式也不相同。
component generator 简单示例
export const generateSwitch = (name, options) => {
const propTypes = {
className: PropTypes.string,
activeKey: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
onChange: PropTypes.func.isRequired,
};
const Switch = (props) => {
...
return (
<span className={classes}>
{
options.map((entry, index) => (
...
))
}
</span>
);
};
Switch.propTypes = propTypes;
Switch.displayName = name;
return Switch;
};
export const ASwitch = generateSwitch('ABSwitch', [
{ name: 'AA', key: 'a' },
{ name: 'BB', key: 'b' },
]);
export const BSwitch = generateSwitch('CDSwitch', [
{ name: 'CC', key: 'c' },
{ name: 'DD', key: 'd' },
]);
解决方案
针对最初的问题,我们在项目中使用了 component generator 来实现:
import React, { useState, useEffect, useCallback } from 'react'
import { Select } from 'antd'
export const enumPickerGenerator = (
// 由于数据格式不同,获取的 select 的 key 和 value 的属性也不同
keyKey,
valueKey,
listGetter,
renderOption,
optionFilter
) => {
return (props) => {
const { onChange, getPopupContainer, value } = props
const [listOptions, setListOptions] = useState([])
useEffect(() => {
const init = async () => {
setLoading(true)
try {
setListOptions(await listGetter())
} catch (e) {
//
}
setLoading(false)
}
init()
}, [])
const emitChange = useCallback(
(latestValue) => {
if (onChange) {
onChange(latestValue)
}
},
[onChange]
)
const onSelectChange = useCallback(
(nextValue) => {
emitChange(nextValue)
},
[emitChange]
)
const customOptionFilter = useCallback(
(input, item) => {
if (!optionFilter) return true
return optionFilter(input, item, listOptions)
},
[listOptions]
)
const renderOptionContent = useCallback(
(opt) => {
if (typeof renderOption === 'function') return renderOption(opt)
if (typeof labelKey === 'string') return opt[labelKey]
return opt[valueKey]
},
[renderOption, labelKey, valueKey]
)
return (
<Select
value={value}
allowClear
onChange={onSelectChange}
filterOption={customOptionFilter}
getPopupContainer={getPopupContainer}
placeholder={`请选择`}
>
{listOptions.map(opt => (
<Select.Option value={opt[valueKey]} key={opt[keyKey || valueKey]}>
{renderOptionContent(opt)}
</Select.Option>
))}
</Select>
)
}
}
最后
以上都是我从同事的代码那儿学来的,感觉很赞的方法。想想有时候也不一定非得买书看教程。看看手头的项目,学习学习其他小伙伴的代码也是很有收获的~
网友评论