- 前端
- UI组件
【免费下载链接】rsuite
🧱 A suite of React components .
导读
MultiCascader是 rsuite 提供的级联多项选择器,用于在具有层级关系的数据中一次选择多个值(例如"地区 → 公司 → 员工"的三级结构)。本指南以 受控模式示例 为主线,讲解如何通过value+onChange让组件状态完全交由 React 状态管理(受控组件),并对比defaultValue非受控用法,深入useCascadeValue源码剖析级联状态下值的归一化规则。读完本文,你将掌握 MultiCascader 受控用法、级联行为差异及源码级原理,可直接复用到表单、搜索筛选等场景。
一、受控模式的完整示例
MultiCascader的受控模式与原生 React 表单控件一致:由外部提供value,组件在用户勾选时通过onChange回传新值,组件自身不再维护选中状态。以下是 controlled.md 中的完整示例:
import { MultiCascader } from 'rsuite'; import { mockTreeData } from './mock'; const data = mockTreeData({ limits: [3, 3, 4], labels: (layer, value, faker) => { const methodName = ['jobArea', 'jobType', 'firstName']; return faker.person[methodName[layer]](); } }); const App = () => { const [value, setValue] = React.useState(['1-1', '1-2']); return <MultiCascader value={value} onChange={setValue} data={data} minw={224} />; }; ReactDOM.render(<App />, document.getElementById('root'));关键点:
value:string[],当前选中的值数组。受控模式下完全由useState等外部状态驱动;onChange:(value: string[], event) => void,用户每次勾选/取消勾选后触发,将归一化后的完整值数组回传。示例中直接传入setValue,写法最简洁;data:必填的层级数据;minw={224}:设置浮层最小宽度,保证列式树形布局有足够展示空间。
初始值['1-1', '1-2']表示第一层第 1 个节点下的两个二级节点默认处于选中状态(数据生成规则见下文)。
二、数据准备:理解mockTreeData的生成规则
示例使用mockTreeData生成三层树形数据,其实现位于 docs/utils/mock.ts:
export function mockTreeData(options: { limits: number[]; labels: string | string[] | ((layer: number, value: string, faker) => string); getRowData?: (layer: number, value: string) => any[]; }) { ... }limits: [3, 3, 4]定义了树的深度与每层节点数量:第一层 3 个节点、第二层每节点 3 个子节点、第三层每节点 4 个子节点;- 值(
value)的生成规则:首层为'1'、'2'、'3',子节点以"父值-序号"拼接,如'1-1'、'1-2'、'1-2-1'。所以示例中的['1-1', '1-2']是第二层的两个节点; labels可传函数,按layer取对应字段名:jobArea(地区/职位领域)→jobType(职位类型)→firstName(人名),配合 faker 生成真实感文案;- 最终每条数据形如
{ label: string, value: string, children: [...] },与组件的默认键名labelKey='label'、valueKey='value'、childrenKey='children'完全对应。
生产环境中你完全可以用自己的接口数据替换mockTreeData,只需保证每条记录包含label、value、children(可用labelKey/valueKey/childrenKey自定义键名)。
三、受控 vs 非受控:value与defaultValue的分工
与value配套,组件还提供了非受控入口defaultValue。对照 default-value.md:
const App = () => ( <div> <p>Cascade:</p> <MultiCascader data={data} defaultValue={['1-1', '1-2', '2']} minw={224} /> <hr /> <p>Not cascaded:</p> <MultiCascader data={data} defaultValue={['1-1', '1-2', '2']} cascade={false} minw={224} /> </div> );两条规则的选用建议:
| 场景 | 推荐用法 |
|---|---|
| 值由 React 状态统一管理(如表单联动、提交前预处理) | value+onChange(受控) |
| 仅需初始选中值、之后交给组件内部维护 | 仅传defaultValue(非受控) |
两者的值都会经过级联归一化处理(见第四节)。从源码看,组件通过useControlled(valueProp, defaultValue)统一管理两种模式,见 src/MultiCascader/MultiCascader.tsx。
四、级联(cascade)与受控值的关系:源码级解析
MultiCascader默认cascade=true,即父子节点双向联动:选中父节点会选中其全部子孙,选中子节点时若兄弟全部选中则自动选中父节点。这直接影响value的形态——级联模式下value会经过归一化,只保留"最顶层"的选中节点。
4.1 归一化逻辑:transformValue
受控值进入组件后,会先经过 src/MultiCascadeTree/hooks/useCascadeValue.ts 中的transformValue处理:
const transformValue = useCallback( (value: T[] = []) => { if (!cascade) { return value; } // ... 逐项 splitValue,收集关联节点与待删除节点 // 最后:若某节点的父节点也在 value 中,则该节点被过滤掉 return nextValue.filter(v => { const item = flattenData.find(n => n[valueKey] === v); if (item?.parent && nextValue.some(v => v === item.parent?.[valueKey])) { return false; } return true; }); }, [cascade, flattenData, splitValue, valueKey] );含义:当cascade=true时,若同时选中了父节点'1'与子节点'1-1',归一化结果只保留'1'——因为父节点已覆盖子孙。这就是示例中defaultValue={['1-1', '1-2', '2']}而非['1', '1-1', ...]的原因。当cascade=false时,value原样保留,允许"父与子各自独立勾选"。
4.2 勾选回调:handleCheck
用户勾选复选框时,useCascadeValue.ts 的handleCheck负责计算下一个值:
if (cascade) { nextValue = splitValue(node, checked, value).value; // 级联:联动增删子孙/祖先 } else { nextValue = [...value]; if (checked) nextValue.push(nodeValue); else nextValue = nextValue.filter(n => n !== nodeValue); // 非级联:仅增删自身 } setValue(nextValue); onChange?.(nextValue, event); // 回传受控回调 onCheck?.(nextValue, node, checked, event); // 额外的勾选明细回调这里有两处值得注意:
onChange始终收到归一化后的完整数组,受控父组件无需自行处理级联细节;onCheck额外提供node(当前勾选项)与checked状态,适合"仅在勾选某类节点时做埋点或联动"的场景。
4.3 受控 + 动态级联开关
cascade.md 展示了一个典型的受控组合:用Toggle动态切换级联,切换时清空已选值:
const App = () => { const [cascade, setCascade] = React.useState(true); const [value, setValue] = React.useState([]); const handleToggle = checked => { setCascade(checked); setValue([]); // 级联规则变化后旧值语义不再成立,主动清空 }; return ( <div> <Toggle checked={cascade} onChange={handleToggle}>Cascade</Toggle> <hr /> <MultiCascader w={280} data={data} value={value} cascade={cascade} onChange={setValue} /> </div> ); };这个模式很有工程价值:当切换级联开关时,setValue([])保证value与新的cascade语义一致,避免"非级联值数组"被带入级联模式造成冗余项。
五、受控行为如何被测试验证
仓库测试 src/MultiCascader/test/MultiCascader.spec.tsx 使用testControlledUnControlled用例同时覆盖受控与非受控两种模式:
testControlledUnControlled(MultiCascader, { componentProps: { data: items, defaultOpen: true }, value: ['1'], defaultValue: ['2'], changedValue: ['3'], simulateEvent: { changeValue: (prevValue: any) => { fireEvent.click(screen.getByRole('checkbox', { name: '3' })); return { changedValue: [...prevValue, '3'] }; } }, expectedValue: (value: string[]) => { const input = screen.getByTestId('picker-toggle-input'); expect(input).to.have.attribute('value', value.toString()); } });测试通过点击复选框模拟用户操作,断言picker-toggle-input的值为最新数组——印证了"受控组件在用户操作后必须把新值同步到value绑定"这一契约。此外,测试还覆盖了testFormControl(与Form.Control集成)以及aria-haspopup="tree"等无障碍属性。
六、受控相关 Props 速查
以下为与受控用法强相关的 Props(完整清单见 英文文档 与 中文文档):
| 属性 | 类型(默认值) | 说明 |
|---|---|---|
value | string[] | 指定已选项的值(受控),数组元素对应数据中的valueKey字段值 |
defaultValue | string[] | 指定默认选中的值(非受控初始值) |
onChange | (value: string[], event) => void | 值变化时触发,返回归一化后的完整数组 |
onCheck | (value: string, item, checked, event) => void | 复选框状态变化时触发,含节点与勾选态明细 |
cascade | boolean(true) | 是否父子节点双向级联,直接影响value的归一化规则 |
data* | Option[] | 必填,层级数据源 |
valueKey/labelKey/childrenKey | string('value'/'label'/'children') | 自定义数据键名 |
uncheckableItemValues | string[] | 不可勾选的节点值(如"只看部门、不选具体人"场景) |
disabledItemValues | string[] | 禁用的节点值 |
cleanable | boolean(true) | 是否允许清空已选值;清空时onChange会收到[] |
七、小结
- 受控核心:
value+onChange,外部状态全权决定选中项,示例见 controlled.md; - 级联归一化:
cascade=true(默认)时值数组只保留顶层节点,由 useCascadeValue.ts 的transformValue与splitValue实现;切换cascade时应同步重置value; - 非受控替代:仅需初始值时使用
defaultValue; - 测试保障:
testControlledUnControlled验证了受控模式下用户操作与value同步的契约,参考 MultiCascader.spec.tsx。
受控模式让MultiCascader可以无缝接入表单状态库(如 Form 组件、Redux、Zustand),并让搜索筛选、级联联动、值预处理等业务逻辑集中在组件外部统一处理,是复杂选择场景下的推荐写法。
- 前端
- UI组件
【免费下载链接】rsuite
🧱 A suite of React components .
相关推荐
rsuite DateRangePicker 受控与非受控模式完全指南:value、onChange 与 defaultValue 实战
rsuite DateRangePicker 受控与非受控模式完全指南:value、onChange 与 defaultValue 实战 本篇技术指南围绕 rs
前端UI组件rsuite DatePicker 受控与非受控模式完全指南:value / defaultValue / onChange 的源码级解析
rsuite DatePicker 受控与非受控模式完全指南:value / defaultValue / onChange 的源码级解析 在 React 生态
前端UI组件RSuite Cascader 受控模式实战:用 value 与 onChange 掌控级联选择状态
RSuite Cascader 受控模式实战:用 value 与 onChange 掌控级联选择状态 本指南聚焦 RSuite 级联选择器(Cascader)的
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考