MantineUI Autocomplete自定义Item组件无法显示问题求助
Mantine Autocomplete自定义Item组件不显示问题排查
按照Mantine UI官方文档实现Autocomplete的自定义Item组件时,出现以下问题:
- 使用业务数据
productsData时,自定义组件无法显示内容 - 替换为文档提供的示例数据后,自定义组件可正常展示
以下是相关代码及问题原因分析、解决方案:
我的实现代码
interface SearchProps extends SelectItemProps { name: string; } const itemSearch = forwardRef<HTMLDivElement, SearchProps>( // eslint-disable-next-line @typescript-eslint/no-unused-vars ({ value, name, ...others }: SearchProps, ref) => ( <div ref={ref} {...others}> <Group> <Text>{name}</Text> <Text>{value}</Text> </Group> </div> ) ); function Search(props: Omit<ComponentProps<typeof Autocomplete>, "data">) { const { products, q } = useLoaderData() as Awaited<ReturnType<typeof loader>>; const submit = useSubmit(); const inputRef = useRef<HTMLFormElement>(null); const productsData = products?.map( (product: { name: string; _id: string }) => ({ name: product.name, value: product._id, label: product.name, }) ); console.log(productsData); return ( <Form role="search" ref={inputRef} method="GET" className={props.className}> <Autocomplete itemComponent={itemSearch} styles={{ input: { borderRadius: "20px !important", }, }} defaultValue={q as string} type="search" aria-label="Search products" placeholder="Search " onChange={() => { const isFirstSearch = q == null; submit(new FormData(inputRef?.current as HTMLFormElement), { replace: !isFirstSearch, }); }} // icon={<IconSearch size="1rem" stroke={2} />} icon={<Loader size="xs" />} name="q" data={productsData ? productsData : []} /> </Form> ); } export default Search;
文档示例代码
const charactersList = [ { image: 'https://img.icons8.com/clouds/256/000000/futurama-bender.png', label: 'Bender Bending Rodríguez', description: 'Fascinated with cooking, though has no sense of taste', }, { image: 'https://img.icons8.com/clouds/256/000000/futurama-mom.png', label: 'Carol Miller', description: 'One of the richest people on Earth', }, { image: 'https://img.icons8.com/clouds/256/000000/homer-simpson.png', label: 'Homer Simpson', description: 'Overweight, lazy, and often ignorant', }, { image: 'https://img.icons8.com/clouds/256/000000/spongebob-squarepants.png', label: 'Spongebob Squarepants', description: 'Not just a sponge', }, ]; const data = charactersList.map((item) => ({ ...item, value: item.label })); interface ItemProps extends SelectItemProps { color: MantineColor; description: string; image: string; } const AutoCompleteItem = forwardRef<HTMLDivElement, ItemProps>( ({ description, value, image, ...others }: ItemProps, ref) => ( <div ref={ref} {...others}> <Group noWrap> <Avatar src={image} /> <div> <Text>{value}</Text> <Text size="xs" color="dimmed"> {description} </Text> </div> </Group> </div> ) );
问题原因及解决方案
1. 数据有效性与结构问题
- 检查
products加载状态:通过console.log(products)确认数据不是undefined或空数组,且每个product的name字段为非空有效值。 - 优化
productsData兜底逻辑:避免products为undefined时生成无效数据,强制兜底为空数组:const productsData = (products || []).map( (product: { name: string; _id: string }) => ({ name: product.name, value: product._id, label: product.name, }) );
2. defaultValue与value字段不匹配
你设置的defaultValue={q as string}是产品名称,但productsData中每个item的value是产品ID,两者类型不匹配会导致Autocomplete无法正确匹配选项,引发渲染异常:
- 方案一:统一
value与defaultValue类型,将productsData的value改为产品名称:const productsData = (products || []).map(product => ({ name: product.name, value: product.name, label: product.name, })); - 方案二:匹配对应产品ID作为
defaultValue:const defaultProduct = products?.find(p => p.name === q); <Autocomplete defaultValue={defaultProduct?._id || ''} // ...其他props />
3. onChange事件触发页面跳转打断渲染
当前onChange事件在用户输入时立即提交表单,会触发页面导航,导致下拉列表未完成渲染就被刷新打断:
- 改为监听
onSelect事件(用户选中下拉选项时触发)再提交表单:<Autocomplete // ...其他props onSelect={() => { const isFirstSearch = q == null; submit(new FormData(inputRef?.current as HTMLFormElement), { replace: !isFirstSearch, }); }} // 移除原有的onChange />
4. 类型定义优化
显式定义productsData的类型,确保字段完整性,避免类型隐式转换问题:
type ProductAutocompleteItem = { name: string; value: string; label: string; }; const productsData: ProductAutocompleteItem[] = (products || []).map( (product: { name: string; _id: string }) => ({ name: product.name, value: product._id, label: product.name, }) );
内容的提问来源于stack exchange,提问作者VN-Boy
相关产品推荐
相关产品推荐

