【问题标题】:How to type forwardRef in separate select component?如何在单独的选择组件中键入 forwardRef?
【发布时间】:2021-02-22 17:24:25
【问题描述】:

我使用的技术是 react、typescript 和 styled-components。 我正在尝试创建选择组件以便在 React Hook Form 中使用它。 起初,看起来一切都是正确的,但我从打字稿中得到一个错误:

没有重载匹配这个调用。 重载 1 of 2, '(props: Pick, "form" | ... 262 更多 ... | "onTransitionEndCapture"> & { ...; } & ISelectProps, " form" | ... 264 更多 ... | "options"> & Partial<...>, "form" | ... 264 更多 ... | "options"> & { ...; } & { ...; }): ReactElement<...>',给出了以下错误。 类型“ForwardedRef”不可分配给类型“RefObject | (RefObject & ((instance: HTMLSelectElement | null) => void)) | (((实例:HTMLSelectElement | null) => void) & RefObject<...>) | ((((实例:HTMLSelectElement | null)=> void)&(((实例:HTMLSelectElement | null)=> void))|空 |不明确的'。 类型“MutableRefObject”不可分配给类型“RefObject | (RefObject & ((instance: HTMLSelectElement | null) => void)) | (((实例:HTMLSelectElement | null) => void) & RefObject<...>) | ((((实例:HTMLSelectElement | null)=> void)&(((实例:HTMLSelectElement | null)=> void))|空 |不明确的'。 类型 'MutableRefObject' 不可分配给类型 '((instance: HTMLSelectElement | null) => void) & RefObject'。 类型“MutableRefObject”不可分配给类型“(实例:HTMLSelectElement | null)=> void”。 类型“MutableRefObject”不匹配签名“(instance: HTMLSelectElement | null): void”。

这是我的代码:

import React, { forwardRef } from "react";
import styled from "styled-components";
import arrow from "../assets/svg/select_arrow.svg";

interface ISelectProps {
  options?: { name: string; value: string | number }[];
  name: string;
  ref?:
    | ((instance: HTMLSelectElement | null) => void)
    | React.RefObject<HTMLSelectElement>
    | null
    | undefined;
}

const SelectContainer = styled.div`
  display: flex;
  align-items: center;
  width: 100%;
  position: relative;
`;

const StyledSelect = styled.select<ISelectProps>`
  width: 100%;
  height: 30px;
  padding: 0 10px;
  background: ${({ theme }) => theme.colors.transparentButton};
  color: white;
  border: 1px solid white;
  border-radius: ${({ theme }) => theme.borderRadius};
  font-size: 14px;
  -moz-appearance: none; /* Firefox */
  -webkit-appearance: none; /* Safari and Chrome */
  appearance: none;
  &:focus,
  &:active {
    outline: none;
  }
`;

const StyledArrow = styled.img`
  position: absolute;
  right: 10px;
  padding: inherit;
`;

const Select = forwardRef(({ options, name }: ISelectProps, ref) => {
  return (
    <>
      <SelectContainer>
        <StyledSelect name={name} ref={ref}>
          {options?.map((op) => (
            <option value={op.value}>{op.name}</option>
          ))}
        </StyledSelect>
        <StyledArrow src={arrow} />
      </SelectContainer>
    </>
  );
});

export default Select;

codesandbox.io/s/eager-hertz-opbsi?file=/src/select.tsx

我做错了什么? 提前感谢所有有用的答案!

【问题讨论】:

标签: javascript reactjs typescript styled-components react-forwardref


【解决方案1】:

首先,ref 在这种情况下不是 props 的一部分。 您应该为forwardRef 提供两个泛型参数以帮助 TS 缩小类型。

ref 是什么?它只是 HTML 元素

import React, { forwardRef } from "react";
import styled from "styled-components";

interface ISelectProps {
  options?: { name: string; value: string | number }[];
  name: string;

}

const SelectContainer = styled.div`
  display: flex;
  align-items: center;
  width: 100%;
  position: relative;
`;

const StyledSelect = styled.select<ISelectProps>`
  width: 100%;
  height: 30px;
  padding: 0 10px;
  background: ${({ theme }) => theme.colors.transparentButton};
  color: white;
  border: 1px solid white;
  border-radius: ${({ theme }) => theme.borderRadius};
  font-size: 14px;
  -moz-appearance: none; /* Firefox */
  -webkit-appearance: none; /* Safari and Chrome */
  appearance: none;
  &:focus,
  &:active {
    outline: none;
  }
`;

const StyledArrow = styled.img`
  position: absolute;
  right: 10px;
  padding: inherit;
`;

const Select = forwardRef<HTMLSelectElement, ISelectProps>(({ options, name }, ref) => {
  return (
    <>
      <SelectContainer>
        <StyledSelect name={name} ref={ref}>
          {options?.map((op) => (
            <option value={op.value}>{op.name}</option>
          ))}
        </StyledSelect>
      </SelectContainer>
    </>
  );
});

export default Select;

Playground link

【讨论】:

  • 哇,非常感谢。这表明我还需要了解很多关于 TS 的知识。我还不完全理解在这些尖括号中输入是如何工作的,但我会尝试分析它。从我看到的顺序很重要( vs )。
  • @zagoor 顺序问题不是因为某些打字稿功能,而是由于类型定义,请查看我的答案以获取更多信息。
  • 你可以在这里阅读泛型typescriptlang.org/docs/handbook/generics.html
【解决方案2】:

您从未输入过ref 对象,请查看React TypeScript Cheatsheet - forwardRef 中的示例。

// T typing the ref
forwardRef<T, P = {}>(...)

// Full typing signature
function forwardRef<T, P = {}>(render: ForwardRefRenderFunction<T, P>): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>;

// BAD - ref object untyped
const Select = forwardRef(({ options, name }: ISelectProps, ref) => {...}

// GOOD - ref object typed
const Select = forwardRef<HTMLSelectElement, ISelectProps>(
  ({ options, name }, ref) => {...}
);

https://codesandbox.io/s/hungry-architecture-7gsnp?file=/src/select.tsx:665-1038

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-11
    • 1970-01-01
    • 2021-11-17
    • 2020-09-10
    相关资源
    最近更新 更多