react-admin筛选变更时清空ReferenceArrayInput选中值问题
- 表单内使用两个关联输入组件,子分类选择组件被
FormDataConsumer包裹,依赖分类选择组件的选中值做条件筛选和显隐控制 - 预期逻辑:选中分类后加载对应子分类选项,修改分类时自动清空已选的子分类值
- 实际异常:选中分类+对应子分类后,重新修改分类值,之前选中的子分类值会残留,不会自动清空
- 影响版本:react-admin 4.1.0 到最新master分支均存在该现象
- 已尝试方案:调用
useFormContext()提供的setValue、resetValue方法均未生效,初步怀疑是数组字段特殊机制或缓存导致
原始实现代码如下:
<ArrayInput source={`shops.${shopIndex}.categories`}> <SimpleFormIterator disableReordering> <ReferenceInput source="categoryId" reference="categories"> <SelectInput optionText="name" /> </ReferenceInput> <FormDataConsumer> {({ formData, getSource, scopedFormData, ...rest }) => { // 未选择分类时不渲染,无法筛选子分类 if (!scopedFormData.categoryId) { return null; } return ( <SelectArrayInput source={getSource('subCategoryIds')} reference="subCategories" perPage={500} filter={{ categories: [scopedFormData.categoryId] }} > <ReferenceArrayInput optionText="name" /> </SelectArrayInput> ); }} </FormDataConsumer> </SimpleFormIterator> </ArrayInput>
当前子分类筛选、组件显隐切换功能均运行正常,仅缺少分类变更时自动清空已选子分类值的逻辑。
ArrayInput嵌套SimpleFormIterator时,每个迭代项的字段路径是带动态数组索引的(例如shops.0.categories.1.subCategoryIds),直接调用全局useFormContext的setValue方法时,若未传入带正确索引的完整字段路径,无法匹配到数组内的目标嵌套字段,方法调用不会生效。同时react-admin默认不会在输入组件因条件渲染卸载时自动清空对应字段值,最终导致旧值残留。
不需要额外引入依赖,直接通过作用域内的路径生成方法拿到子分类的完整字段路径,再调用表单更新方法清空值即可,两种实现方式可按需选择:
方案1:给分类选择器增加变更回调
将分类选择器也包裹在FormDataConsumer中,给SelectInput增加onChange事件,每次分类值变更时,先清空当前迭代项的subCategoryIds值,再更新分类值:
<ArrayInput source={`shops.${shopIndex}.categories`}> <SimpleFormIterator disableReordering> <FormDataConsumer> {({ getSource, ...rest }) => ( <ReferenceInput source="categoryId" reference="categories"> <SelectInput optionText="name" onChange={(value) => { // 通过getSource生成当前迭代项子分类的完整路径,清空旧值 rest.setValue(getSource('subCategoryIds'), []); // 更新选中的分类值 rest.setValue(getSource('categoryId'), value); }} /> </ReferenceInput> )} </FormDataConsumer> <FormDataConsumer> {({ formData, getSource, scopedFormData, ...rest }) => { if (!scopedFormData.categoryId) { return null; } return ( <SelectArrayInput source={getSource('subCategoryIds')} reference="subCategories" perPage={500} filter={{ categories: [scopedFormData.categoryId] }} > <ReferenceArrayInput optionText="name" /> </SelectArrayInput> ); }} </FormDataConsumer> </SimpleFormIterator> </ArrayInput>
注意不要使用全局useFormContext返回的setValue,要使用FormDataConsumer透传的setValue,配合getSource生成的路径才能准确命中数组内的嵌套字段。
方案2:子分类渲染逻辑增加关联校验
在FormDataConsumer渲染子分类组件的逻辑中,判断如果当前已选的子分类不属于新选中的分类,直接清空对应值即可,适合子分类和分类关联校验逻辑更复杂的场景。
注意:不要通过给组件加key强制重挂载的方式清空值,会导致选项缓存丢失、接口重复请求,性能损耗更高。
内容的提问来源于stack exchange,提问作者Wesley Overdijk

