需求:用户在选择第一个选择框的选项后,第二个选择框的选项会根据第一个选择框的选择动态更新。如图所示
出现的问题
一级分类选择之后二级分类没有数据,第二次重新选择一级分类的时候,二级分类就会有值。
第一次点击截图:
第二次点击截图:
解决方法
因为之前的代码使用我们自己封装的ProTable组件,最后没有使用组件,而是直接使用ProTable,没有报错
代码如下:
const HeadSlideManager = () => {
// 其他部分省略
const [firstClassify, setFirstClassify] = useState([]);//一级分类
const [secondClassify, setSecondClassify] = useState([]);//二级分类
const [valueEnumList, setValueEnumList] = useState([]);//二级分类所有数据
useEffect(() => {
const fetchData = async () => {
try {
// const res = await getClassify(1) 请求一级分类数据
const res = await API.getFirstClassify();
if (res.code === 0) {
setFirstClassify(res.data.map(item => ({ value: item.id, label: item.name })))
} else {
console.error('Error fetching data:', res.message);
}
// const res = await getClassify(2) 请求二级分类数据
const ress = await API.getSecondClassify();
if (ress.code === 0) {
setValueEnumList(ress.data) // 保存二级分类所有数据
} else {
console.error('Error fetching data:', res.message);
}
} catch (error) {
console.error('Error fetching data:', error);
}
}
fetchData();
}, [])
const handleFirstClassifyChange = (value) => {
setTimeout(() => {
// 设置具体的二级分类数据,也就是根据一级分类定二级分类
setSecondClassify(valueEnumList.filter(item => item.pid === value).map(item => ({ value: item.id, label: item.name })))
}, 10)
}
const columns = [
{
title: <FormattedMessage id="pages.cms.table.video.head.slide.classify.1" />,
dataIndex: 'first_classify',
ellipsis: true,
align: 'center',
valueType: 'select', // 表单搜索设置为选择框
fieldProps: {
options: firstClassify,
onChange: handleFirstClassifyChange,
},
},
{
title: <FormattedMessage id="pages.cms.table.video.head.slide.classify.2" />,
dataIndex: 'second_classify',
ellipsis: true,
align: 'center',
valueType: 'select',
fieldProps: {
options: secondClassify,
},
},
]
return (
<PageHeaderWrapper ghost={false}>
<div>
<ProTable
columns={columns}
rowKey="id"
options={false}
request={API.videoHeadSlide}
pagination={{
pageSize: 10,
current: 1,
showSizeChanger: true,
showQuickJumper: true,
pageSizeOptions: [10, 20, 30]
}}
toolBarRender={() => [addButton]}
scroll={{ x: 'max-content' }}
/>
</div>
</PageHeaderWrapper>
);
};
export default HeadSlideManager;
我们封装的ProTable组件
const PaginationTable = ({
request: defaultRequest,
onChange,
pagination: defaultPagination,
params: defaultParams,
columns: defaultColumns,
form: defaultForm,
queryTransfer,
actionRef: defaultAction,
...props
}) => {
const aRef = useRef();
const actionRef = defaultAction || aRef;
const [first, setFirst] = useState(true); // 第一次进入页面
const [pagination, setPagination] = useState({
pageSize: Number(history.location.query.size || 10),
current: Number(history.location.query.page || 1),
showQuickJumper: true,
size: 'default',
...defaultPagination,
});
const [lastSorter, setLastSorter] = useState(
history.location.query.sort || history.location.query.sort_by
? {
sort: history.location.query.sort,
sort_by: history.location.query.sort_by,
}
: undefined,
);
const correctQuery = ({ page = 1, size = 10, ...other }) => {
setPagination({ ...pagination, current: page, pageSize: size });
history.replace({ query: { page, size, ...other } });
};
const request = async ({ current, pageSize: size, ...p }, rSorter, filter) => {
setFirst(false);
let page = current;
if (first) {
// fix: 第一次进入页面表单提交导致page变1
page = pagination.current;
if (actionRef.current?.pageInfo) actionRef.current.pageInfo.current = page;
}
let sorter;
if (rSorter) {
const keys = Object.keys(rSorter);
if (keys.length > 0) {
sorter = { sort: { ascend: '1', descend: '-1' }[rSorter[keys[0]]], sort_by: keys[0] };
}
}
setLastSorter(sorter);
if (!simpleIsSame(lastSorter, sorter)) {
page = 1;
}
const notEmptyParams = { ...p, page, size };
Object.keys(notEmptyParams).forEach((key) => {
const val = notEmptyParams[key];
if (val === null || val === undefined || val.toString().length === 0) {
delete notEmptyParams[key];
}
});
correctQuery({ ...notEmptyParams, ...sorter });
if (defaultRequest) {
const res = await defaultRequest({ ...notEmptyParams, ...sorter, ...filter });
if (res.code === 0) {
const total = typeof res.total === 'number' ? res.total : 0;
if (!res.data?.length) {
let prevPage = page - 1;
if (typeof res.total === 'number') {
const totalPage = Math.ceil(res.total / size);
if (prevPage > totalPage) prevPage = totalPage;
}
if (page > 1) page = prevPage;
}
setPagination((currentPg) => ({ ...currentPg, current: page, total }));
}
return res;
}
return { success: false, data: [] };
};
// query由form, sorter, pagination和params组成
const getFormAndParamsFromQuery = () => {
const { page, size, sort, sort_by: sortBy, ...query } = history.location.query;
Object.keys(query).forEach((q) => {
if (query[q] === '') {
query[q] = undefined;
}
});
return query;
};
const [form] = Form.useForm();
const [params, setParams] = useState(defaultParams);
useEffect(() => {
const queryParams = getFormAndParamsFromQuery();
if (first) {
// 第一次进入设置表单数据
let fieldsValue;
if (queryTransfer) {
fieldsValue = queryTransfer(queryParams);
} else {
fieldsValue = {};
Object.keys(queryParams).forEach((k) => {
if (defaultColumns?.find((c) => c.dataIndex === k)) fieldsValue[k] = queryParams[k];
});
}
if (fieldsValue && Object.keys(fieldsValue).length > 0) {
form?.setFieldsValue(fieldsValue);
form?.submit(); // page会变1
}
} else {
// 参数修改后设置页数为1
let same;
if (!defaultParams) same = false;
else
same = Object.keys(defaultParams).every((k) => {
if (queryParams[k] === defaultParams[k]) return true;
if (Array.isArray(defaultParams[k]) && typeof queryParams[k] === 'string')
return defaultParams[k].length === 1 && defaultParams[k][0] === queryParams[k];
if (Array.isArray(defaultParams[k]) && Array.isArray(queryParams[k]))
return defaultParams[k].join(',') === queryParams[k].join(',');
return false;
});
if (!same) setPagination({ ...pagination, current: 1 });
}
setParams(defaultParams);
}, [defaultParams]);
const columns = useMemo(() => {
const { query } = history.location;
if (query.sort_by)
return defaultColumns.map((v) => {
if (v.sorter && query.sort_by === v.dataIndex) {
return { ...v, defaultSortOrder: { 1: 'ascend', '-1': 'descend' }[query.sort] };
}
return { ...v, defaultSortOrder: undefined };
});
return defaultColumns;
}, [defaultColumns]);
return (
<ProTable
params={params}
request={request}
onChange={(tPagination, filter, sorter, extra) => {
setPagination(tPagination);
if (onChange) onChange(tPagination, filter, sorter, extra);
}}
actionRef={actionRef}
form={{ form, autoFocusFirstInput: false, ...defaultForm }}
pagination={pagination.current === 0 ? undefined : pagination}
columns={columns}
search={{ defaultCollapsed: false }}
tableAlertRender={false}
revalidateOnFocus={false}
{...props}
/>
);
};
export default PaginationTable;
错误分析过程
通过查看第一次点击截图,我发现第一次valueEnumList没有值,第二次选择一级分类的时候valueEnumList有值,所以我首先怀疑是这段代码引起的问题,因为在React中setXxx方法是一个异步函数,可能导致在 handleFirstClassifyChange 函数中的 valueEnumList 并没有及时更新到最新的状态。
seEffect(() => {
const fetchData = async () => {
try {
const ress = await API.getSecondClassify();
if (ress.code === 0) {
// 这行代码引发问题
setValueEnumList(ress.data) //
} else {
console.error('Error fetching data:', res.message);
}
} catch (error) {
console.error('Error fetching data:', error);
}
}
fetchData();
}, [])
最后根据百度,gpt提示,使用 useEffect 来监听 valueEnumList 的变化,并在变化后更新 secondClassify 的状态,同时使用函数式更新来设置 secondClassify 状态。函数式更新可以确保在设置状态时访问到的状态是最新的。也就是添加下面的代码
// 监听 valueEnumList 变化,更新 secondClassify
useEffect(() => {
// 默认第一个选择框的值为空,第二个选择框也应该为空
setSecondClassify([]);
}, [valueEnumList]);
const handleFirstClassifyChange = (value) => {
// 使用函数式更新确保访问到的 valueEnumList 是最新的状态
setSecondClassify(prevSecondClassify => {
// 根据第一个选择框的值筛选出第二个选择框的选项
return valueEnumList.filter(item => item.pid === value).map(item => ({ value: item.id, label: item.name }));
});
};
添加之后还是不行,最后试了各种方法,一句话就是不行。
讲讲最后为什么突然使用原生的ProTable,而没有使用封装的ProTable(* 原理还没有弄明白)
前面自己百度,gpt那么多,都没有解决,只好找博士师兄帮忙,师兄也是研究了很长时间,最后不知道为什么点到封装的ProTable组件页面,就从头到尾看了一遍代码,然后就怀疑是useMemo这个Hooks影响的,最后就尝试使用原生的ProTable,然后发现就没有问题了。师兄就忙自己的事了。
const columns = useMemo(() => {
const { query } = history.location;
if (query.sort_by)
return defaultColumns.map((v) => {
if (v.sorter && query.sort_by === v.dataIndex) {
return { ...v, defaultSortOrder: { 1: 'ascend', '-1': 'descend' }[query.sort] };
}
return { ...v, defaultSortOrder: undefined };
});
return defaultColumns;
}, [defaultColumns]);
工程人嘛,先解决问题,最后再研究原理(嘿嘿嘿)