如何制作一个可由react-hook-forms控制的通用文本字段?

Lys*_* Ng 4 reactjs typescript-generics react-hook-form

上下文:我正在使用react-hook-forms 创建文本字段列表(由控制器控制)。我发现我正在编写相当多的类似代码,并考虑创建一个通用文本字段。

我的问题有两个:

  1. 如何传递填充name文本字段的界面属性?
  2. 如何通过 props 成功传递通用对象类型以在利用 React-hook-forms 的组件中使用?

我的代码如下:

interface GenericTextfieldProps<T extends FieldValues> {
    formLabel: string;
    name: keyof T;
    control: Control<T>;
};

function GenericTextfield<T extends FieldValues>({
    formLabel,
    name,
    control,
}: GenericTextfieldProps<T>) {
    return (
        <Stack direction="column" spacing={0.5}>
            <Controller
                render={({ field }) => (
                    <TextField
                        {...field}
                        id="textfield"
                        label=""
                        size="small"
                        fullWidth
                        sx={{ ml: 3 }}
                        disabled
                        variant="outlined"
                    />
                )}
                name={name}
                control={control}
            />
        </Stack>
    );
}
Run Code Online (Sandbox Code Playgroud)

但是,当我翻译代码以使其更加通用(即通过通用类型进行解析)时, name={name}开始抛出以下字符串错误:

Type 'keyof T' is not assignable to type 'Path<T>'.

对于第一季度:

首先我尝试了 props 中的 name: string 。这不起作用,因为react-hook-forms需要实际的密钥来允许控制器发挥其魔力。

我从9 个月前的这篇文章中确定我可以keyof {my typescript object}在道具中使用。这是非常成功的。

但是,当我翻译代码以使其更加通用(即通过通用类型进行解析)时, name={name}开始抛出以下字符串错误:

Type 'keyof T' is not assignable to type 'Path<T>'.

进一步研究react-hook-forms,似乎有某种相对字段路径需要来自实际打字稿对象的键和值。我也认为这个 stackoverflow 帖子是相关的,但不幸的是那里没有我可以参考的答案。

我认为这与表单如何找到键和值的路径有关(从这里的controller.d.ts文件中收集到:

export declare type UseControllerProps<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> = {
    name: TName;
    rules?: Omit<RegisterOptions<TFieldValues, TName>, 'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'>;
    shouldUnregister?: boolean;
    defaultValue?: FieldPathValue<TFieldValues, TName>;
    control?: Control<TFieldValues>;
};
Run Code Online (Sandbox Code Playgroud)

理想情况下,如果有人之前这样做过,我会寻找一种以更通用的方式使用react-hook-forms的优雅解决方案。注意:我不打算使用keyOf任何其他解决方案/想法,也欢迎。

小智 7

你走在正确的轨道上UseControllerProps。

UseControllerProps<T>通过使用whereT扩展了props 接口FieldValues,您现在可以设置您想要使用的 Controller props。

import Stack from '@mui/material/Stack';
import TextField from '@mui/material/TextField';
import { Controller, FieldValues, UseControllerProps } from 'react-hook-form';

interface GenericTextfieldProps<T extends FieldValues>
    extends UseControllerProps<T> {
    formLabel: string;
    disabled?: boolean;
}

function GenericTextfield<T extends FieldValues>({
    name,
    control,
    formLabel,
    disabled = false,
}: GenericTextfieldProps<T>) {
    return (
        <Stack direction="column" spacing={0.5}>
            <Controller
                render={({ field }) => (
                    <TextField
                        {...field}
                        id="textfield"
                        label={formLabel}
                        size="small"
                        fullWidth
                        sx={{ ml: 3 }}
                        disabled={disabled}
                        variant="outlined"
                    />
                )}
                name={name}
                control={control}
            />
        </Stack>
    );
}
Run Code Online (Sandbox Code Playgroud)