【问题标题】:React nested forwardRef反应嵌套的 forwardRef
【发布时间】:2022-01-08 21:33:50
【问题描述】:

在我的 React (w/ typescript) 应用程序中,我使用 react-hook-form 创建了一个表单来管理它的所有逻辑。

然后我用一些 css 和其他东西自定义了 select 元素。但是,为了问题的简单起见,这里是准系统组件:

import { forwardRef } from 'react';

type Props = React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLSelectElement>, HTMLSelectElement>;

const Select = forwardRef<HTMLSelectElement, Props>((props, ref) => {
    return (
        <select ref={ref} {...props}>
            <option value="">...</option>
            {props.children}
        </select>
    );
});

export default Select;

然后我定义了另一个“专门化”前一个组件的组件,如:

import { forwardRef } from 'react';

import Select from './select';

type Props = React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLSelectElement>, HTMLSelectElement> & { label: string; errors?: string };

const SelectWitLargeData = forwardRef<HTMLSelectElement, Props>((props, ref) => {
    return (
        <Select ref={ref} {...props}>
           ...large amount of options
        </Select>
    );
});

export default SelectWitLargeData;

问题是编译器在

上给了我一个错误
Type 'ForwardedRef<HTMLSelectElement> | LegacyRef<HTMLSelectElement>' is not assignable to type 'Ref<HTMLSelectElement>'.
  Type 'string' is not assignable to type 'Ref<HTMLSelectElement>'.ts(2322)
index.d.ts(140, 9): The expected type comes from property 'ref' which is declared here on 

type 'IntrinsicAttributes & Pick<Props, "label" | "key" | keyof InputHTMLAttributes<HTMLSelectElement> | "errors"> & RefAttributes<...>'

(JSX attribute) RefAttributes<HTMLSelectElement>.ref?: Ref<HTMLSelectElement>

我在网上搜索了解决方案,找到了很多建议,但这些都不起作用。

任何帮助表示赞赏 提前致谢!

编辑 这是使用组件的示例表单

import { useEffect } from 'react';
import { useForm } from 'react-hook-form';

import Select from '@components/select';
import SelectWithLargeData from '@components/select-with-large-data';

type Form = {
    email: string;
    state: string;
    country: string;
};

const Address: React.FC = () => {
    const { handleSubmit, register, setValue } = useForm<Form>();

    useEffect(() => {
        setValue('state', 'AL');
    }, [setValue]);

    const onSubmit = async (input: Form) => {
        console.log(input);
    };

    return (
        <>
            <form onSubmit={handleSubmit(onSubmit)} className="space-y-10">
                <input type="email" {...register('email', { required: true })} />

                <SelectWithLargeData label="Provincia" {...register('state', { required: true })} />

                <Select label="Stato" {...register('country', { required: true })}>
                    <option value="IT">Italia</option>
                </Select>
            </form>
        </>
    );
};

export default Address;

编辑 2 https://codesandbox.io/s/withered-rain-w9ej4


【问题讨论】:

    标签: reactjs typescript react-hooks react-hook-form


    【解决方案1】:

    请参阅forwarding-refs 文档:

    const FancyButton = React.forwardRef((props, ref) => (
      <button ref={ref} className="FancyButton">
        {props.children}
      </button>
    ));
    
    // You can now get a ref directly to the DOM button:
    const ref = React.createRef();
    <FancyButton ref={ref}>Click me!</FancyButton>;
    

    您应该在SelectWitLargeData 中创建ref,而不是再次转发。

    考虑这个例子:

    import React, { forwardRef } from 'react';
    
    type Props = React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLSelectElement>, HTMLSelectElement>;
    
    const Select = forwardRef<HTMLSelectElement, Props>((props, ref) => {
      return (
        <select ref={ref} {...props}>
          <option value="">...</option>
          {props.children}
        </select>
      );
    });
    
    type SelectHigherOrder = Props & { label: string; errors?: string };
    
    const SelectWitLargeData = (props: Omit<SelectHigherOrder, 'ref'>) => {
      const ref = React.useRef<HTMLSelectElement>(null);
    
      return (
        <Select ref={ref} {...props}>
          1
        </Select>
      );
    }
    

    Playground

    您可能已经注意到我使用了Omit&lt;SelectHigherOrder, 'ref'&gt;。这是因为您使用的是这种类型:React.DetailedHTMLProps&lt;React.InputHTMLAttributes&lt;HTMLSelectElement&gt;, HTMLSelectElement&gt;;。并且这种类型包含旧的 ref 类型:type WithLegacyRef = Props['ref'] 并且当您在 ref={ref} 之后使用 {...props} 时,您实际上覆盖了正确的 ref 类型,即 React.RefObject&lt;HTMLSelectElement&gt;

    【讨论】:

    • 几乎完美。但是从 react-hook-form SetValue 设置一个值在第一页加载时不起作用,我需要刷新以选择正确的选项
    • @Valerio 你没有在你的问题中提到这个要求。我对您的组件结构一无所知。请为您的问题提供更多背景信息
    • 第一行已经说明了:) 但是现在在浏览器控制台中给我这个错误Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
    • 在主要问题中添加了示例表单
    • @Valerio 对不起,我不明白。我只问过打字稿编译错误,对吧?你能分享一下反应沙箱的链接吗?我无法重现错误
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-27
    • 2022-08-19
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 2019-06-04
    • 2020-08-06
    相关资源
    最近更新 更多