You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ReactJS 实现双API结果比对的分页下拉选择组件出现接口死循环问题

问题原因分析

你的代码存在3个核心问题直接导致了异常表现:

  • React的setState是异步更新的,你在调用setListaRealm后立刻读取listaRealm拿到的永远是更新前的旧值,所以你返回的选项永远是上一页的旧数据,状态看起来没有更新
  • 你在loadOptions中主动修改组件状态listaRealm,状态更新触发组件重渲染,AsyncPaginate组件重渲染后会重新调用loadOptions接口,直接造成接口循环调用
  • hasMore判断逻辑完全错误:listaRealm.length >= 1意味着只要存在数据就永远认为还有下一页,永远触发下一页加载,同时没有对两个接口的分页状态做判断,完全不符合你的业务需求
修复方案
const SelectAsyncPaginate = props => {
    const [realmScelto, setRealmScelto] = useState(null);

    useEffect(() => {
        setRealmScelto(props.realmScelto);
    }, [props.realmScelto]);

    const loadOptions = async (searchQuery, loadedOptions, { page }) => {
        // 并行请求两个接口提升加载速度
        const [optionToShow, optionToShow2] = await Promise.all([
            fetch(
                `${apiUrl}?page=${page}&size=20&deletedDate.specified=false${searchQuery.length > 3 ? `&appDesapplicazione.contains=${searchQuery}` : ''}`
            ).then(res => res.json()),
            fetch(
                `${apiUrlRealm}?page=${page}&size=20&deletedDate.specified=false`
            ).then(res => res.json())
        ]);

        // 过滤需要展示的选项
        const notPresent = optionToShow2.content.filter(ra => {
            return !optionToShow.content.some(rua => rua.realm.id === ra.id && rua.user.perCod === props.persona.perCod.perCod);
        });

        // 判断是否还有下一页:两个接口任意一个还有未加载数据就继续翻页,这里假设接口返回Spring Data分页结构,last为true代表是最后一页
        const hasMore = !optionToShow.last || !optionToShow2.last;

        return {
            // 直接返回当前页处理好的选项即可,AsyncPaginate会自动拼接历史选项
            options: notPresent,
            hasMore: hasMore,
            additional: {
                // 搜索关键词变化时重置页码,否则页码累加
                page: page + 1,
            },
        };
    };

    const onChange = option => {
        if (typeof props.onChange === 'function') {
            props.onChange(option);
        }
    };

    return (
        <AsyncPaginate
            value={props.value}
            loadOptions={loadOptions}
            // 注意:过滤后的notPresent是realm对象本身,不需要加.realm层级
            getOptionValue={option => option.id}
            getOptionLabel={option => option.realmId}
            onChange={onChange}
            isSearchable={true}
            placeholder="Seleziona Realm"
            additional={{
                page: 0,
            }}
            // 搜索时重置页码,避免用之前的页码加载
            debounceTimeout={300}
        />
    );
};

注:代码中假设你的分页接口返回的是Spring Data标准分页结构,包含content(当前页数据数组)、last(是否为最后一页)字段,如果你接口的分页字段不同,替换为对应字段即可,比如hasNextPage、totalPages等。

关键修改点说明
  • 移除了自行维护的listaRealm状态:AsyncPaginate组件会自动管理已加载的所有选项,不需要手动维护,避免状态更新触发的循环调用
  • 修正选项取值逻辑:过滤后的选项是realm对象本身,修复了原代码中取option.realm.id无法获取值的问题
  • 修正hasMore判断逻辑:根据两个接口的实际分页返回结果判断是否还有下一页,不会无限制加载
  • 并行请求两个接口:减少请求等待时间,提升组件响应速度
  • 新增搜索防抖:避免输入过程中频繁触发接口请求

内容的提问来源于stack exchange,提问作者Jack23

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 08:24:04