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

封装MUI TypeScript Autocomplete组件以返回指定属性标量值

封装MUI Autocomplete:将值映射为对象指定属性类型

需求场景

MUI Autocomplete默认绑定的value和onChange参数类型是其options数组中对象的完整类型。但实际业务中,表单往往只需要提交对象的某个标量属性(比如id),而非整个对象。我们需要封装一个组件,让value和onChange的参数直接对应到指定属性的类型,同时兼容multiple属性(开启时value变为该标量的数组类型)。

组件实现

import React, { useCallback } from 'react';
import Autocomplete, { AutocompleteProps } from '@mui/material/Autocomplete';

type WrappedAutocompleteProps<
  T extends object,
  K extends keyof T,
  Multiple extends boolean = false
> = Omit<AutocompleteProps<T, Multiple>, 'value' | 'onChange'> & {
  valueProp: K;
  value: Multiple extends true ? T[K][] : T[K] | null;
  onChange: (value: Multiple extends true ? T[K][] : T[K] | null) => void;
};

export const WrappedAutocomplete = <
  T extends object,
  K extends keyof T,
  Multiple extends boolean = false
>({
  valueProp,
  value,
  onChange,
  options,
  multiple,
  ...rest
}: WrappedAutocompleteProps<T, K, Multiple>) => {
  // 把外部传入的标量值转换为Autocomplete需要的对象
  const internalValue = useCallback(() => {
    if (multiple) {
      return (value as T[K][])?.map(id => options.find(opt => opt[valueProp] === id)).filter(Boolean);
    }
    return options.find(opt => opt[valueProp] === value);
  }, [value, options, valueProp, multiple]);

  // 把Autocomplete返回的对象转换为外部需要的标量值
  const internalOnChange = useCallback(
    (_event: React.SyntheticEvent, newValue: Multiple extends true ? T[] : T | null) => {
      if (multiple) {
        onChange((newValue as T[])?.map(item => item[valueProp]) ?? []);
      } else {
        onChange(newValue ? newValue[valueProp] : null);
      }
    },
    [onChange, valueProp, multiple]
  );

  return (
    <Autocomplete
      {...rest}
      multiple={multiple}
      options={options}
      value={internalValue()}
      onChange={internalOnChange}
    />
  );
};

使用示例

import { useState, useCallback } from 'react';
import { WrappedAutocomplete } from './WrappedAutocomplete';

// 定义选项对象类型
interface Film {
  id: number;
  label: string;
}

// 示例选项数组
const top100Films: Film[] = [
  { id: 1, label: 'The Shawshank Redemption' },
  { id: 2, label: 'The Godfather' },
  // ...其他影片
];

// 单选场景
const Demo = () => {
  const [value, setValue] = useState<number | null>(null);

  const handleChange = useCallback((selectedId: number | null) => {
    setValue(selectedId);
    // 直接拿到id用于表单提交
    console.log('选中的影片ID:', selectedId);
  }, []);

  return (
    <WrappedAutocomplete<Film, 'id'>
      valueProp="id"
      options={top100Films}
      value={value}
      onChange={handleChange}
      renderInput={(params) => <input {...params} placeholder="选择影片" />}
    />
  );
};

// 多选场景
const MultiDemo = () => {
  const [values, setValues] = useState<number[]>([]);

  const handleChange = useCallback((selectedIds: number[]) => {
    setValues(selectedIds);
    console.log('选中的影片ID列表:', selectedIds);
  }, []);

  return (
    <WrappedAutocomplete<Film, 'id', true>
      valueProp="id"
      multiple={true}
      options={top100Films}
      value={values}
      onChange={handleChange}
      renderInput={(params) => <input {...params} placeholder="多选影片" />}
    />
  );
};

关键说明

  • 泛型T对应选项对象的类型,K对应要提取的属性键,Multiple标记是否开启多选
  • internalValue负责将外部传入的标量值(或数组)转换为Autocomplete需要的对象(或对象数组)
  • internalOnChange负责将Autocomplete返回的对象(或对象数组)转换为外部需要的标量值(或数组)
  • 完全兼容MUI Autocomplete的原生属性,只需额外传入valueProp指定映射属性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 04:20:31