持续更新中 2025-2-20
1. 既能输入不存在的选项又能进行下拉框选择
展开
2. menu 组件无法动态设置 defaultSelectedKeys
展开
何时需要动态修改 defaultSelectedKeys?删除默认选中的菜单,需要让第一个菜单选中。
改变 defaultSelectedKeys 是无效的,因为它只有在首次渲染才会生效,我们需要使用到 selectedKeys。
// MenuWrapper.tsx
const [selectedKeys, setSelectedKeys] = React.useState<string[]>();
React.useEffect(() => {
// 修复:当defaultSelectedKeys变化时,选中的菜单项没有变化,因为 defaultSelectedKeys 只有初次渲染时生效
// 当外界更新 defaultSelectedKeys 时,需要手动更新 selectedKeys
// 如此设计是为了简化,否则调用者需要同时提供 defaultSelectedKeys 和 selectedKeys
setSelectedKeys(defaultSelectedKeys);
}, [defaultSelectedKeys]);
return <Menu
defaultSelectedKeys={defaultSelectedKeys}
selectedKeys={selectedKeys}
onSelect={({ key }) => setSelectedKeys([key])}
/>
完整代码
<Menu
className={styles.menu}
style={{ width: 200, border: 0 }}
defaultSelectedKeys={defaultSelectedKeys}
selectedKeys={selectedKeys}
defaultOpenKeys={defaultOpenKeys}
onSelect={({ key }) => setSelectedKeys([key])}
expandIcon={() => null}
mode={'inline'}
inlineIndent={8}
items={items}
/>
能否只用 selectedKeys?不能,否则用户点击菜单的时候不能选中了,此时还得将 onSelect 暴露给调用方让其主动设置 selectedKeys,而这里我们只需要暴露 defaultSelectedKeys 即可,用 useEffect 做同步。故需结合 defaultSelectedKeys 和 selectedKeys 一起使用。
使用更简单,内部处理了菜单点击高亮逻辑,只需传入 defaultSelectedKeys:
<MenuWrapper defaultSelectedKeys={[...]} />
如果只用 selectedKeys 调用复杂:
<MenuWrapper selectedKeys={[...]} onSelect={onSelect} defaultSelectedKeys={[...]} />
3. 顶部带有搜索表单的表格,如何做到输入变化就自动发起搜索
展开
答案:onValuesChange 结合 debounce。
<Form
onValuesChange={() => {
queryWithConditions({ pageNumber: current, pageSize });
}}
...
>
...
</Form>
// buttons 搜索 重置 新增等
// 主体内容 <Table
但是还不够,我们得做 debounce 省去无谓请求。
我们将之前的 queryWithConditions 重命名成 queryWithConditionsCore,然后将其包裹在 useDebounceFn,其他都不用变。
import { useDebounceFn } from 'ahooks';
// 组件内代码
const { run: queryWithConditions } = useDebounceFn(queryWithConditionsCore, { wait: 500 });
4. ProTable 如何让搜索按钮和自定义按钮比如“创建”同一行
展开
官方文档潦草了事,咋整?
如果你只是想新增按钮,直接一行代码搞定
optionRender: (searchConfig, props, dom) => dom.concat(creatingNode),
如果需要对“重置”或“搜索”按钮做调整可以如此操作:
search: {
span: 4,
labelWidth: 0,
// 将搜索按钮和创建按钮放到同一行
optionRender: ({ searchText, resetText }, { form }, [_resetBtn, _searchBtn]) => [
// 重置按钮
<Button
key="reset"
onClick={() => {
form?.resetFields();
form?.submit();
}}
>
{resetText}
</Button>,
// 搜索按钮
<Button
key="sub"
onClick={() => {
form?.submit();
}}
// 给查询按钮增加 type 和 ghost 让其和新增按钮区分开来
type="primary"
ghost
>
{searchText}
</Button>,
// 新增按钮
<Button
key="button"
type="primary"
onClick={() => {
history.push({
pathname: '/foo/create'
});
}}
>
{t('common.action.create')}
</Button>,
],
}
若并未对重置按钮做任何修改,可以直接复用 resetBtn:
...
// 将搜索按钮和创建按钮放到同一行
optionRender: ({ searchText }, { form }, [resetBtn, _searchBtn]) => [
// 重置按钮原封不动
resetBtn,
...
如果只是为了微调,比如给查询按钮新增 prop ghost 可以使用 React.cloneElement
optionRender: (_, config, [resetBtn, queryBtn]) => {
return [
resetBtn,
// @ts-expect-error 新增 prop `ghost`
React.cloneElement(queryBtn, { ghost: true }),
];
},
参考
5. 如何识别当前项目运行的 antd 版本
展开
import { version } from 'antd';
console.log('当前Ant Design版本:', version); // 4.x.x 5.x.x
6. Radio 需要同时传输 name 和 id 给服务端如何实现
展开
hidden 表单的妙用。
hidden 表单也会提交给服务端,只是 UI 上不可见,刚好满足我们的需求,这也是以前 web 的常规技巧比如 csrf_token 的提交。
<Form.Item
label={'资源组')}
name="name"
required
>
<Radio.Group
options={nameOpts}
optionType="button"
// 🔥 1. 同步更新 name 对应的 id
onChange={() => {
const name = form.getFieldValue('name');
setHiddenFieldForId(name);
}}
>
<Radio />
</Radio.Group>
</Form.Item>
<!-- 🔥 2. hidden 表单也会提交给服务端,只是 UI 上不可见,刚好满足我们的需求 -->
<Form.Item name="id" hidden>
<Input />
</Form.Item>
如果有默认值需要初始化一次
function setHiddenFieldForId(name: string) {
const id = nameOpts.find(item => item.label === name)?.id;
form.setFieldValue('id', id);
}
const initialId = nameOpts?.[0]?.value;
if (initialId && !form.getFieldValue('id')) {
setHiddenFieldForId(initialId);
}
7. 没有修改,保存按钮需置灰,如何实现
8. 没有搜索按钮,输入即可发起请求,如何实现
展开
第一感觉是用 Form 的 onChange 但是如果开启 allowClear 用户点击清除按钮该事件不会触发,需要用 onValuesChange。当然更好是叠加 debounce 。
其实 antd 并没有
onChange文档,但该事件确实存在。
import { PlusOutlined } from '@ant-design/icons';
import { useDebounceFn } from 'ahooks';
import { Form, Input, Space, Button } from 'antd';
import React from 'react';
interface IProps {
onCreate: () => void;
onChange: (params: { name: string; label: string }) => void;
}
export const SearchFilter: React.FC<IProps> = ({ onCreate, onChange }) => {
const [form] = Form.useForm();
// 🔥 关键点 2
const { run: onFormChange } = useDebounceFn(
() => {
const values = form.getFieldsValue();
console.log('SearchFilter onChange', values);
onChange(values);
},
{ wait: 600 },
);
return (
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<Form
// 🔥 关键点 1
onValuesChange={onFormChange}
layout="inline"
form={form}
style={{ marginBottom: 24, flex: '1 0 auto' }}
>
<Form.Item name={'name'}>
<Input style={{ width: 328 }} maxLength={200} placeholder="请输入名称" allowClear />
</Form.Item>
<Form.Item name={'label'}>
<Input style={{ width: 328 }} maxLength={200} placeholder="请输入标签" allowClear />
</Form.Item>
</Form>
<Space size={'middle'} style={{ alignItems: 'start' }}>
<Button icon={<PlusOutlined />} onClick={onCreate} type="primary">
创建
</Button>
</Space>
</div>
);
};
9. 一行排列的组件中间用垂直分隔线隔开如何实现
展开
9.1 通过 index > 0 使得首尾没有分割线。
import React from 'react';
import { Divider } from 'antd';
export type IFeeUI = { title: string; };
interface IProps {
list: IFeeUI[];
}
export const ListWithSeparator: React.FC<IProps> = ({ list }) => {
return (
<div style={{ display: 'flex', gap: 40, flexWrap: 'wrap', }} >
{list.map((item, index) => (
// 🔥 1. React.Fragment 包裹多个组件
<React.Fragment key={item.id}>
{/* 🔥 2. 通过 index 判断使得开头没有分割线 */}
{index > 0 && (
<Divider type="vertical" style={{ margin: 0, alignSelf: 'center' }} />
)}
<Component data={item}> 组件 </Component>
</React.Fragment>
))}
</div>
);
};
9.2 forEach 结合 pop
10. 如何修改 popover / tooltip 箭头颜色
答案:通过 CSS 变量。该方式适用于很多 antd 样式覆盖场景。
CSS 变量可以让自定义 CSS 变得非常容易无需再写 !important 或重复多个类名增加权重的代码,建议大家团队内组件库也如此设计。
第一步:找到 CSS 变量
点击变量可以发现是写在 class 上的:
第二步:给其父元素增加 CSS 变量
'--antd-arrow-background-color': '#4b6de8'
因为 CSS 变量的查找也是有作用域的,可以理解为“就近原则”,antd 默认变量都定义在对应的 class 上,或则 :root 上,如果我们的 CSS 变量定义在 style 上则权重会更高。
<Tooltip
styles={{
root: {
// @ts-expect-error 通过 css 变量修改箭头颜色
['--antd-arrow-background-color']: '#4b6de8',
},
body: {
...
},
}}
>
{children}
</Tooltip>