0%

ant-design-select-no-placeholder

介绍

今天在写代码的时候,用到了Ant Design的Select组件,也就是下拉选择框,和以前一样,习惯性的给Select组件加上了placeholder,但是运行程序时,发现Select组件中并未显示placeholder。我的代码如下,大家看出问题来了吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { Form, Select } from 'antd';

export default function SelectComponent() {
const options: { label: string; value: string }[] = [
{ label: 'Option 1', value: 'option1' },
{ label: 'Option 2', value: 'option2' },
{ label: 'Option 3', value: 'option3' },
];

return (
<Form initialValues={{ select: '' }} layout="vertical">
<Form.Item label="Select an option" name="select">
<Select
options={options}
style={{ width: 200 }}
placeholder="Select an option"
allowClear
/>
</Form.Item>
</Form>
);
}

问题分析

没错,罪魁祸首就是这个initialValues={{ select: '' }},因为我在Form组件中设置了Select组件初始值为'',注意initialValues中小写的select对应Form.Item中组件的name,不要与大写的Select名字搞混了,这就导致该组件的值被设置为空字符串,从而无法显示placeholder

如果把这个initialValues去掉,或者将其设置为undefined,就可以正常显示placeholder了。

反过来思考一下,placeholder的作用是在用户尚未选择某个值的时候给用户一个提示,而空字符串从程序的角度来说,是一个合法的值,从而导致Ant Design的Select组件不会显示placeholder

但是undefined就不一样了,它表示没有值,这时Select组件就会显示placeholder。(还是基础不牢呀)。

源码分析

为了追本溯源,我们从源码的角度分析一下其中细节,首先去Ant Design的github页面将源码下载到本机。https://github.com/ant-design/ant-design

然后找到Select组件的源码,路径是src/components/select。在这个目录下,我们可以找到index.tsx文件。这就是Select组件的入口了。

于是尝试在文件中搜索placeholder,竟然没找到,这时我才意识到,Ant Design的Select组件并没有直接处理placeholder,而是通过rc-select这个库来实现的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const InternalSelect = <OptionType extends BaseOptionType | DefaultOptionType = DefaultOptionType>() {
//...
return (
<RcSelect<any, any>
ref={ref as any}
virtual={virtual}
dropdownMatchSelectWidth={dropdownMatchSelectWidth}
{...selectProps}
// ...
/>
);
};

// ...
const Select = React.forwardRef(InternalSelect);
export default Select;

从上面的代码可以看出,InternalSelect组件内容是通过RcSelect来实现的,而RcSelect是一个来自rc-select库的组件。遂又去github上找到这个库的源码 - https://github.com/react-component/select。

全库搜索placeholder,很快找到了相关代码,下面是placeholder对应的结点的定义,最终Select通过渲染这个结点来显示placeholder

1
2
3
4
5
6
7
8
9
10
11
12
13
const placeholderNode = React.useMemo<React.ReactNode>(() => {
if (item) {
return null;
}
return (
<span
className={`${prefixCls}-selection-placeholder`}
style={hasTextInput ? { visibility: 'hidden' } : undefined}
>
{placeholder}
</span>
);
}, [item, hasTextInput, placeholder, prefixCls]);

if(item) - 这句很快被排除,因为item是当前选中的值,而我们要显示placeholder的前提是没有选中任何值。

于是来到style这一行,可以看到如果hasTextInputtrue,则placeholder的样式会被设置为visibility: 'hidden' - 也就是隐藏。

1
style={ hasTextInput ? { visibility: 'hidden' } : undefined }

继续查找hasTextInput的定义,发现它是通过inputValue来判断的。

1
const hasTextInput = mode !== 'combobox' && !open && !showSearch ? false : !!inputValue;

继续查找inputValue的定义,最终发现如下代码,看到了吗?这里添加了空字符串,也就是说如果inputValue是空字符串,那么hasTextInput就会被设置为true,从而导致placeholder被隐藏。

1
let inputValue: string = searchValue || '';

打完收工,觉得有用就点个关注,你的关注是我输出的动力!我们明天再见!