首页
/ Mantine MultiSelect 组件异步数据加载问题解析

Mantine MultiSelect 组件异步数据加载问题解析

2025-05-06 00:23:51作者:霍妲思

问题背景

在使用 Mantine UI 库的 MultiSelect 组件时,开发者经常会遇到需要从异步数据源加载选项的情况。一个典型场景是当用户输入搜索关键词时,组件需要从 API 获取匹配的选项数据。然而,这种异步加载模式可能会导致已选中的选项显示异常,特别是在搜索结果变化时。

核心问题分析

MultiSelect 组件在显示已选中的标签时,会执行以下逻辑:

  1. 在选项列表中查找与选中值匹配的项
  2. 如果找到匹配项,则显示其标签(label)
  3. 如果未找到匹配项,则直接显示值(value)

当使用异步数据源时,常见的问题是:

  • 初始加载时获取的数据可能不包含之前已选中的项
  • 搜索结果变化时,之前加载的数据可能被清除
  • 组件无法在变化的选项列表中保持已选项的标签显示

解决方案实现

数据状态管理

解决这个问题的关键在于维护一个完整的数据状态,确保它包含:

  1. 当前搜索结果的选项
  2. 之前已选中项的选项
const [data, setData] = useState<Product[]>([]);
const [values, setValues] = useState<string[]>([]);

数据合并逻辑

实现一个数据合并函数,确保新获取的数据不会覆盖已存在的数据:

const composeOption = useCallback((items: Product[]) => {
  setData((prevData) => {
    const newItems = items.filter(
      (item) => !prevData.some((existingItem) => existingItem.id === item.id)
    );
    return [...prevData, ...newItems];
  });
}, []);

数据清理优化

在获取新数据前,可以清理掉不再需要的选项,但保留已选中的项:

const removeUnusedOptions = useCallback(() => {
  setData((prevData) => prevData.filter((item) => values.includes(item.id)));
}, [values]);

数据格式化

将原始数据格式化为 MultiSelect 需要的结构:

const formatDataForDropdown = useMemo(
  () =>
    data.map((product) => ({
      value: product.id,
      label: `${product.name} - ${product.company}`,
    })),
  [data]
);

完整实现示例

结合上述思路,一个完整的异步 MultiSelect 组件实现如下:

const ProductSelect = ({ onSelect, ...props }) => {
  const [data, setData] = useState<Product[]>([]);
  const [values, setValues] = useState<string[]>([]);
  const [searchValue, setSearchValue] = useState('');
  const [debouncedSearch] = useDebouncedValue(searchValue, 300);

  const { data: products, error, isValidating } = useSWR(
    debouncedSearch ? `/api/products/?q=${encodeURIComponent(debouncedSearch)}` : null,
    fetcher
  );

  // 数据合并函数
  const composeOption = useCallback((items: Product[]) => {
    setData((prevData) => {
      const newItems = items.filter(
        (item) => !prevData.some((existingItem) => existingItem.id === item.id)
      );
      return [...prevData, ...newItems];
    });
  }, []);

  // 数据清理函数
  const removeUnusedOptions = useCallback(() => {
    setData((prevData) => prevData.filter((item) => values.includes(item.id)));
  }, [values]);

  // 数据格式化
  const formatDataForDropdown = useMemo(
    () =>
      data.map((product) => ({
        value: product.id,
        label: `${product.name} - ${product.company}`,
      })),
    [data]
  );

  // 数据加载效果
  useEffect(() => {
    if (products) {
      removeUnusedOptions();
      composeOption(products);
    }
  }, [products, removeUnusedOptions, composeOption]);

  // 选中项变化回调
  useEffect(() => {
    onSelect(values);
  }, [values, onSelect]);

  return (
    <MultiSelect
      data={formatDataForDropdown}
      value={values}
      onChange={setValues}
      label="Products"
      placeholder="Type to search for products..."
      searchValue={searchValue}
      onSearchChange={setSearchValue}
      rightSection={isValidating ? <Loader size="1rem" /> : null}
      checkIconPosition="right"
      searchable
      clearable
      error={error ? 'Error fetching products' : undefined}
      {...props}
    />
  );
};

最佳实践建议

  1. 数据持久化:考虑将已选中的数据持久化到本地存储或状态管理库中
  2. 性能优化:对于大量数据,实现虚拟滚动或分页加载
  3. 错误处理:完善错误处理逻辑,提供友好的用户反馈
  4. 去重策略:根据业务需求调整数据合并策略
  5. 自定义渲染:利用 Mantine 的渲染属性自定义选项和选中项的显示

通过这种实现方式,可以确保 MultiSelect 组件在异步数据场景下稳定工作,同时保持良好的用户体验。

登录后查看全文
热门项目推荐
相关项目推荐

热门内容推荐

最新内容推荐

项目优选

收起
ohos_react_nativeohos_react_native
React Native鸿蒙化仓库
C++
176
261
RuoYi-Vue3RuoYi-Vue3
🎉 (RuoYi)官方仓库 基于SpringBoot,Spring Security,JWT,Vue3 & Vite、Element Plus 的前后端分离权限管理系统
Vue
860
511
ShopXO开源商城ShopXO开源商城
🔥🔥🔥ShopXO企业级免费开源商城系统,可视化DIY拖拽装修、包含PC、H5、多端小程序(微信+支付宝+百度+头条&抖音+QQ+快手)、APP、多仓库、多商户、多门店、IM客服、进销存,遵循MIT开源协议发布、基于ThinkPHP8框架研发
JavaScript
93
15
openGauss-serveropenGauss-server
openGauss kernel ~ openGauss is an open source relational database management system
C++
129
182
openHiTLSopenHiTLS
旨在打造算法先进、性能卓越、高效敏捷、安全可靠的密码套件,通过轻量级、可剪裁的软件技术架构满足各行业不同场景的多样化要求,让密码技术应用更简单,同时探索后量子等先进算法创新实践,构建密码前沿技术底座!
C
259
300
kernelkernel
deepin linux kernel
C
22
5
cherry-studiocherry-studio
🍒 Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端
TypeScript
596
57
CangjieCommunityCangjieCommunity
为仓颉编程语言开发者打造活跃、开放、高质量的社区环境
Markdown
1.07 K
0
HarmonyOS-ExamplesHarmonyOS-Examples
本仓将收集和展示仓颉鸿蒙应用示例代码,欢迎大家投稿,在仓颉鸿蒙社区展现你的妙趣设计!
Cangjie
398
371
Cangjie-ExamplesCangjie-Examples
本仓将收集和展示高质量的仓颉示例代码,欢迎大家投稿,让全世界看到您的妙趣设计,也让更多人通过您的编码理解和喜爱仓颉语言。
Cangjie
332
1.08 K