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

如何扩展适配React-hook-form的Mantine MultiSelect以支持数值类型?

问题描述

我正在使用React-hook-form结合Mantine MultiSelect组件与Zod进行开发,已经封装了适配React-hook-form的MultiSelect组件:

import {
  type UseControllerProps,
  useController,
  type FieldValues,
} from "react-hook-form";
import {
  MultiSelect as $MultiSelect,
  type MultiSelectProps as $MultiSelectProps,
} from "@mantine/core";

export type MultiSelectProps<T extends FieldValues> = UseControllerProps<T> &
  Omit<$MultiSelectProps, "value" | "defaultValue">;

export function MultiSelect<T extends FieldValues>({
  name,
  control,
  defaultValue,
  rules,
  shouldUnregister,
  onChange,
  ...props
}: MultiSelectProps<T>) {
  const {
    field: { value, onChange: fieldOnChange, ...field },
    fieldState,
  } = useController<T>({
    name,
    control,
    defaultValue,
    rules,
    shouldUnregister,
  });

  return (
    <$MultiSelect
      value={value}
      onChange={(e) => {
        fieldOnChange(e);
        onChange?.(e);
      }}
      error={fieldState.error?.message}
      {...field}
      {...props}
    />
  );
}

目前为了通过Zod验证,我把下拉选项的value转成了字符串:

const dropDownResourcesValues = resources.map((x: any) => ({
  label: x.id.toString(),
  value: x.id.toString(),
}));

<MultiSelect
  style={{ width: "50%" }}
  required
  size="md"
  radius="md"
  label="Tags"
  withAsterisk
  placeholder="Please enter tags"
  name={"tags"}
  data={dropDownTagValues}
  control={control}
/>

但后端接口要求POST请求中该字段为数值类型,请问是否可以扩展MultiSelectProps以支持数值作为选项值?


解决方案

当然可以扩展封装的组件来支持数值类型的选项值,核心思路是在组件内部做字符串与数值的双向转换,既满足Mantine MultiSelect默认的字符串值要求,又能让React-hook-form的字段值保持为数值类型,同时兼容Zod验证和后端接口需求。

步骤1:扩展组件Props,添加类型转换配置

给MultiSelectProps新增一个可选参数valueType,用来指定最终提交的字段类型(string或number),同时明确泛型支持:

import {
  type UseControllerProps,
  useController,
  type FieldValues,
} from "react-hook-form";
import {
  MultiSelect as $MultiSelect,
  type MultiSelectProps as $MultiSelectProps,
} from "@mantine/core";

// 新增类型转换选项
type ValueType = "string" | "number";

export type MultiSelectProps<T extends FieldValues> = UseControllerProps<T> &
  Omit<$MultiSelectProps, "value" | "defaultValue"> & {
    valueType?: ValueType;
  };

步骤2:修改组件逻辑,实现双向类型转换

在组件内部处理value和onChange的转换逻辑:

  • 传给Mantine组件的显示值统一转成字符串(适配组件要求)
  • 用户选择后的值根据valueType转成对应类型,同步给React-hook-form
  • 保持默认逻辑兼容,默认值仍为string类型
export function MultiSelect<T extends FieldValues>({
  name,
  control,
  defaultValue,
  rules,
  shouldUnregister,
  onChange,
  valueType = "string", // 默认字符串类型
  ...props
}: MultiSelectProps<T>) {
  // 预处理默认值类型
  const processedDefaultValue = defaultValue
    ? valueType === "number"
      ? (defaultValue as unknown as string[]).map(Number)
      : defaultValue
    : undefined;

  const {
    field: { value, onChange: fieldOnChange, ...field },
    fieldState,
  } = useController<T>({
    name,
    control,
    defaultValue: processedDefaultValue,
    rules,
    shouldUnregister,
  });

  // 转成Mantine组件需要的字符串值
  const displayValue = valueType === "number"
    ? (value as unknown as number[]).map(String)
    : value;

  return (
    <$MultiSelect
      value={displayValue}
      onChange={(selectedValues) => {
        // 转成最终提交需要的类型
        const finalValues = valueType === "number"
          ? selectedValues.map(Number)
          : selectedValues;
        
        fieldOnChange(finalValues);
        onChange?.(finalValues);
      }}
      error={fieldState.error?.message}
      {...field}
      {...props}
    />
  );
}

步骤3:使用组件时指定数值类型

现在可以直接使用数值类型的选项数据,无需手动转字符串,组件内部会自动处理转换:

// 直接保留数值作为选项value
const dropDownResourcesValues = resources.map((x: any) => ({
  label: x.id.toString(),
  value: x.id, // 这里用原始数值类型
}));

<MultiSelect
  style={{ width: "50%" }}
  required
  size="md"
  radius="md"
  label="Tags"
  withAsterisk
  placeholder="Please enter tags"
  name={"tags"}
  data={dropDownResourcesValues}
  control={control}
  valueType="number" // 指定提交值为数值类型
/>

步骤4:Zod验证配置

此时Zod可以直接验证数值数组,无需再针对字符串处理:

import { z } from "zod";

const formSchema = z.object({
  tags: z.array(z.number()).nonempty("请选择至少一个标签"),
});

这样既满足了Mantine组件的显示需求,又能让React-hook-form的字段值保持为数值类型,完美适配后端接口要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 12:26:29