ant.design中Select数据回填未达到预期的解决方案
2020-11-28 本文已影响0人
悟C
遇到的问题
在使用ant UI库中的Select组件时,你是否有遇到编辑表单回填数据无法达到预期效果的问题?
问题场景
例如: 渲染Select组件的数据是通过一个api得到的分页数据[{id: 1, name: '选项1'}, {id: 2, name: '选项2'}]
, 我们提交给服务端的数据是列表中的id,当这个id不在这个默认列表数据里面的时候, 就会造成Select.Option只显示一个id。
问题分析
Select的defaultValue不存在Select.Option中,导致填充好后只展示一个id, 而不是展示name
解决思路
填充表单之前拿到当前id对应数据,插入到渲染Select组件数据的列表中,注意进行数据去重操作
解决方式
这种场景还是比较多见, 避免重复劳动我们通过实现两个hook自动处理数据补充
-
useEffectForm
该hook主要是挟持setFieldValue
, 当触发这个方法的时候调用一个我们自定义的钩子onSetFilesValue
方法。渲染列表的数据我们就是通过该方法进行补充, 代码如下:
import { useRef } from 'react';
import { Form } from 'antd';
function useEffectForm() {
const [form] = Form.useForm();
const stack = useRef();
if (!Array.isArray(status.current)) {
stack.current = [];
}
const proxyForm = new Proxy(form, {
get: (target, name) => {
/**
* 挟持setFieldsValue
* 当调用该方法的时候, 遍历执行我们收集好的事件集合
*/
if (name === 'setFieldsValue') {
return (...args) => {
const firstArgs = args.length ? args[0] : {};
stack.current.forEach((item) => {
const key = item.key;
const callback = item.callback;
const value = firstArgs[key];
typeof callback === 'function' && callback(value);
})
return target[name](...args);
}
}
/**
* 自定义一个onSetFilesValue方法, 接受两个参数key, callback
* 自定一个监听事件集合, 检测到调用该方法, 就把回调插入事件集合中, 在触发setFieldsValue的时候调用
*/
if (name === 'onSetFilesValue') {
return (...args) => {
const callback = args.pop();
const firstArg = args.length && args[0] || null;
if (typeof callback !== 'function' && firstArg) {
return target[name];
}
stack.current.push({ key: firstArg, callback: callback });
}
}
return target[name];
}
});
return [proxyForm];
}
export default useEffectForm;
- 实现一个
useGetList
的hook,用来获取Select渲染的列表数据,默认获取一个list,在触发setFieldValue的时候进行详情获取, 插入到默认的列表中。
import { useState, useEffect } from 'react';
function useList(props) {
const { getList = () => {}, getDetail = () => {}, key="id" } = props;
const [optoins, setOptions] = useState([]);
const [defaultOption, setDefaultOption] = useState();
useEffect(() => {
fetchList();
}, []);
useEffect(() => {
// useEffectForm自定义的方法
props.form && props.form.onSetFilesValue(props.name, (value) => {
value && fetchDetail(value);
});
}, [props.form]);
useEffect(() => {
if (optoins && defaultOption) {
// 去重检测
let isExsit = optoins.findIndex((item) => item[key] === defaultOption[key]) > -1;
if (!isExsit) {
setOptions(optoins.concat([defaultOption]));
}
}
}, [optoins, defaultOption]);
const fetchList = async (name) => {
const data = await getList(name);
if (Array.isArray(data)) {
setOptions(data);
}
}
const fetchDetail = async (id) => {
if (defaultOption) return;
let data = await getDetail(id);
if (toString.call(data) === '[object Object]') {
setDefaultOption(data);
}
}
return [optoins, fetchList];
}
export default useProjectList;
- 使用方式
import React from 'react';
import { Select, Form } from 'antd';
import useProjectList from './useProjectList';
import { getProjectList, getProjectDetail } from '../../service.js';
const SelectProject = ({ name='projectName', form, ...args}) => {
const [projectList, fetchProjectList] = useList({
...args,
form,
name,
key: 'id', // 默认为id, 用来比对set进去的value是否已经存在
fetchList: async (value) => { // 拉取列表的函数,必须返回array, 或者不处理
const res = await getProjectList({
filter: {
title: value || ''
},
search: true,
});
if (res.code !== 0) return;
return res.data;
},
fetchDetail: async(id) => { // 获取指定id的详情,必须返回对象, 或者不处理
const res = await getProjectDetail(id);
if (res.code) return;
return res.data;
}
});
return (
<Form.Item
label="项目"
name={name}
>
<Select
placeholder="请选择项目"
allowClear
showSearch
onSearch={fetchProjectList}
filterOption={false}
>
{
projectList.map((item) => {
return (
<Select.Option
key={item.id}
value={item.id}
>
{item.title}
</Select.Option>
)
})
}
</Select>
</Form.Item>
)
}
export default SelectProject;
form部分的使用
const [form] = useEffectForm();
....
<Form>
<SelectProject form={form} />
</Form>
....
这样我们就可以比较优雅的解决这个问题, 而不需要每次在setFieldValue的时候处理Select.Option是否有当前数据项