☰
rsuite MultiCascader 受控模式完全指南:value、onChange 与级联值的正确用法
2026/9/29 3:03:39 网站建设 项目流程
  • 前端
  • UI组件

【免费下载链接】rsuite

🧱 A suite of React components .

项目地址:https://gitcode.com/gh_mirrors/rs/rsuite
点击查看免费下载

导读

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(完整清单见 英文文档 与 中文文档):

属性类型(默认值)说明
valuestring[]指定已选项的值(受控),数组元素对应数据中的valueKey字段值
defaultValuestring[]指定默认选中的值(非受控初始值)
onChange(value: string[], event) => void值变化时触发,返回归一化后的完整数组
onCheck(value: string, item, checked, event) => void复选框状态变化时触发,含节点与勾选态明细
cascadeboolean(true)是否父子节点双向级联,直接影响value的归一化规则
data*Option[]必填,层级数据源
valueKey/labelKey/childrenKeystring('value'/'label'/'children')自定义数据键名
uncheckableItemValuesstring[]不可勾选的节点值(如"只看部门、不选具体人"场景)
disabledItemValuesstring[]禁用的节点值
cleanableboolean(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 .

项目地址:https://gitcode.com/gh_mirrors/rs/rsuite
点击查看免费下载
上一篇:5分钟掌握哔咔漫画下载器:打造你的专属离线漫画图书馆终极指南
下一篇:m4s-converter:如何永久保存B站视频的完整指南

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

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

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

立即咨询