antd Select 联动选择实践:省市联动中的双 Select 受控级联与 Cascader 选型
2026/9/10 13:30:40 网站建设 项目流程

antd Select 联动选择实践:省市联动中的双 Select 受控级联与 Cascader 选型

【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/GitHub_Trending/an/ant-design

省市联动是 Web 表单里最常见的联动选择场景之一:用户先选择省份,第二个下拉框中的城市列表随之刷新。antd 官方在 Select 组件的联动(coordinate)示例中给出了这一经典用法的标准实现,并明确指出:当遇到这一类依赖型多级选择时,团队更推荐直接使用 Cascader(级联选择) 组件。本文以 ant-design 仓库中该 demo 为骨架,讲解用两个 Select 通过"受控 value + onChange 级联刷新"实现联动的完整写法,再对比 Cascader 方案的适用边界,帮你为"省 → 市 → 区"这类需求做出正确的组件选型。

一、场景定位:什么样的需求属于"联动选择"

先看官方示例的定义(components/select/demo/coordinate.md):

省市联动是典型的例子,联动场景我们更推荐使用 Cascader 组件。

所谓联动选择,指的是后一级选项的内容由前一级的当前值动态决定,而非所有数据一次性平铺。典型例子包括:

  • 省 → 市、市 → 区(行政区划三级联动);
  • 一级分类 → 二级分类 → 三级分类;
  • 品牌 → 型号 → 配置项。

这类数据天然具有树形的包含关系(Zhejiang包含HangzhouNingbo……),因此官方给出两条路径:数据规模与层级较浅、选项自由灵活时用Select 联动;而"省市区"这类层级固定且一次选到底的数据,直接使用Cascader体验更好。

二、官方示例:双 Select 受控联动实现

ant-design 在 Select 组件的 联动 demo 中给出了可运行的最小实现,其完整代码结构如下:

import React, { useState } from 'react'; import { Select, Space } from 'antd'; const cityData = { Zhejiang: ['Hangzhou', 'Ningbo', 'Wenzhou'], Jiangsu: ['Nanjing', 'Suzhou', 'Zhenjiang'], }; type CityName = keyof typeof cityData; const provinceData: CityName[] = ['Zhejiang', 'Jiangsu']; const App: React.FC = () => { const [cities, setCities] = useState(cityData[provinceData[0] as CityName]); const [secondCity, setSecondCity] = useState(cityData[provinceData[0]][0] as CityName); const handleProvinceChange = (value: CityName) => { setCities(cityData[value]); setSecondCity(cityData[value][0] as CityName); }; const onSecondCityChange = (value: CityName) => { setSecondCity(value); }; return ( <Space wrap> <Select defaultValue={provinceData[0]} style={{ width: 120 }} onChange={handleProvinceChange} options={provinceData.map((province) => ({ label: province, value: province }))} /> <Select style={{ width: 120 }} value={secondCity} onChange={onSecondCityChange} options={cities.map((city) => ({ label: city, value: city }))} /> </Space> ); }; export default App;

页面初始渲染结果中,第一个 Select 显示Zhejiang,第二个 Select 自动显示Hangzhou(快照断言可参考 demo.test.tsx.snap 中的renders components/select/demo/coordinate.tsx correctly),完整还原了这一交互。

2.1 数据结构:用映射表描述包含关系

联动数据用"对象映射 + 数组"组织,比扁平数组更贴合"省份包含城市"的语义:

const cityData = { Zhejiang: ['Hangzhou', 'Ningbo', 'Wenzhou'], Jiangsu: ['Nanjing', 'Suzhou', 'Zhenjiang'], }; type CityName = keyof typeof cityData; // 'Zhejiang' | 'Jiangsu' const provinceData: CityName[] = ['Zhejiang', 'Jiangsu'];

这里有两个值得借鉴的类型技巧:

  1. keyof typeof cityData推导出联合类型,让provinceDatahandleProvinceChange的入参在编译期就被约束为合法的省份名,杜绝手写字符串拼错;
  2. provinceData用数组单独维护展示顺序,与映射表的键顺序解耦,未来加"Anhui"只需改一处映射。

2.2 状态设计:一个"非受控 + 一个受控"

示例中的两个 Select 采用差异化状态策略,这是联动组件的关键设计:

  • 省份 Select 用defaultValue(非受控):省份只需记录"当前选中值",无需在外部强制回写;选择后仅靠onChange触发下游刷新。
  • 城市 Select 用value(受控):城市完全由cities状态派生,初始值与省份默认值强一致(cityData[provinceData[0]][0]),保证首屏不出现"省份选了 Zhejiang、城市却是空的"这类状态裂缝。

从源码看,antd 的 Select 本身是一个将valuedefaultValueonChange等属性透传给@rc-component/select的封装(components/select/index.tsx),onChange在选中后回调,应用层据此更新 state 即可完成"受控刷新"闭环。valuedefaultValue的完整类型、语义可查 Select API 文档。

2.3 级联重置:切换上游后如何复位下游

联动最容易被忽略的是脏数据处理——用户先在浙江选了"温州",再切到江苏,此时"温州"并不存在于江苏的城市列表里。示例的handleProvinceChange同时做了两件事:

const handleProvinceChange = (value: CityName) => { setCities(cityData[value]); // 1. 刷新下游选项池 setSecondCity(cityData[value][0]); // 2. 把下游值复位到新省份的第一个城市 };

即每次省份变化,城市列表和当前选中值一起重置。示例选择"取新列表第一个城市"作为复位值,保证永远有效;如果业务上希望复位为空并展示 placeholder,则改成setSecondCity(undefined),并在城市 Select 上配置placeholder。这条"先更新选项、再同步选中值"的顺序是避免出现非法组合值的关键。

2.4 选项渲染:统一走 options 数据源

两个 Select 都使用options属性以对象数组形式传选项:

options={provinceData.map((province) => ({ label: province, value: province }))} options={cities.map((city) => ({ label: city, value: city }))}

{ label, value }[]的写法比 JSX 声明选项性能更好,是官方推荐的首选形式(见 Select API 中 options 字段说明)。外层用 Space 组件 包裹两个 Select,并开启wrap,窄屏下会自动换行。为城市 Select 设置value后,切换省份瞬间触发重渲染,第二个下拉框内选项随之替换,联动即完成。

三、为什么要联动用 Cascader?官方建议背后的理由

示例文档用"我们更推荐使用 Cascader"明确表达了组件选型倾向。对照 Cascader 组件文档 的 "When To Use" 可归纳出该建议的适用前提:

  • 需要从一组具有关联关系的数据集中选择,如省/市/区、公司层级、事物分类;
  • 数据量较大且存在多级分类,需要分级展开以降低单屏选项密度;
  • 在单个浮层内完成多级选择,交互路径更短。

可见"省市区"这类数据有三个特征——层级固定(最多三级)、各级强依赖、最终只需要一个叶子值——恰好是 Cascader 的主场。它用一个浮层内的多列级联菜单呈现全路径,用户无需经历"两级下拉框先后弹出"的心智切换。

Cascader 实现省市区级联

Cascader 实现同级联只需声明一棵嵌套children的树,数据即结构,无需任何联动逻辑。仓库中 Cascader 基础示例 展示了标准形态:

import type { CascaderProps } from 'antd'; import { Cascader } from 'antd'; type Option = { value: string; label: string; children?: Option[] }; const options: Option[] = [ { value: 'zhejiang', label: 'Zhejiang', children: [ { value: 'hangzhou', label: 'Hangzhou', children: [{ value: 'xihu', label: 'West Lake' }], }, ], }, // ... ]; const App: React.FC = () => ( <Cascader options={options} onChange={(value) => console.log(value)} placeholder="Please select" /> );

数据通过children自描述层级,onChange回调一次性返回完整路径(如['zhejiang', 'hangzhou', 'xihu']),服务端直接拿到全链路编码。该示例的官方表述即"省市区级联"(Cascader demo basic.md)。

四、两种方案的选型对照与迁移要点

维度双 Select 联动Cascader
数据形态各级独立扁平 + 映射表关联单棵嵌套children
联动逻辑需自行维护 state、刷新选项并复位下游值内置,零额外代码
选择结果各级值分散在多个控件onChange返回完整层级路径
交互形态下拉依次出现,可分别独立操作/搜索单浮层多列级联菜单
适用场景层级浅、每级选项独立、可能要跨级取用/部分选择省市区/组织层级等强依赖多级选择
搜索支持每级 Select 单独开启showSearchCascader 单列搜索受限,官方注明loadData不能与showSearch同用
动态加载每级分别接接口刷新loadData按需懒加载子节点

迁移到 Cascader 时最值得注意的一点是数据结构的改造:把"省列表 + 各市列表"重构为一棵{ value, label, children }嵌套树;原先每级 Select 的onChange处理逻辑可整体删除,改由 Cascader 统一回调路径。若场景确实需要"先选省再独立操作城市、每级可搜索",才值得保留双 Select 联动方案——此时直接复用本文第二节的受控实现即可。

五、质量保证:示例如何被自动化测试覆盖

该示例不止是文档装饰,它已纳入 Select 组件的自动化回归体系。在快照文件 components/select/tests/snapshots/demo.test.tsx.snap 中,renders components/select/demo/coordinate.tsx correctly会断言首屏结构:渲染出包含两个ant-select-singleant-space容器,分别展示标题为ZhejiangHangzhou的选中值;demo-extend.test.ts.snap 则验证其在扩展 context 下仍能正常渲染。这意味着示例的 DOM 结构、初始状态与受控行为一旦发生破坏性变更,CI 会立即暴露问题,你可以放心地把本文代码作为自定义联动实现的起点并保持该模式持续可测。

小结

  • 省市联动是依赖型联动的经典案例,双 Select 方案的核心是"映射表数据结构 + 下游受控 + onChange 级联刷新与复位",完整可运行实现见 coordinate.tsx;
  • 当数据层级固定且强依赖时,官方立场明确:优先使用 Cascader,它以嵌套数据换掉全部联动代码,并在单浮层内完成多级选择;
  • 选型决策取决于"每级是否要独立交互/搜索、结果是否要完整路径、层级深度"三个问题,本文第四节对照表可作为快速判断依据。

【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/GitHub_Trending/an/ant-design

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询