如何在函数组件中的React中使用泛型?

hro*_*nro 6 generics typescript reactjs react-props

在基于类的组件中,我可以轻松地编写如下代码:

import * as React from 'react';
import { render } from 'react-dom';

interface IProps<T> {
    collapsed: boolean;
    listOfData: T[];
    displayData: (data: T, index: number) => React.ReactNode;
}

class CollapsableDataList<T> extends React.Component<IProps<T>> {
    render () {
        if (!this.props.collapsed) {
            return <span>total: {this.props.listOfData.length}</span>
        } else {
            return (
                <>
                    {
                        this.props.listOfData.map(this.props.displayData)
                    }
                </>
            )
        }
    }
}

render(
    <CollapsableDataList
        collapsed={false}
        listOfData={[{a: 1, b: 2}, {a: 3, b: 4}]}
        displayData={(data, index) => (<span key={index}>{data.a + data.b}</span>)}
    />,
    document.getElementById('root'),
)
Run Code Online (Sandbox Code Playgroud)

实际上这个CollapsableDataList组件应该是一个功能组件,因为它是无状态的,但是我无法弄清楚如何编写一个函数组件并在道具中使用泛型,对我有什么建议吗?

Oka*_*dag 20

反应 18,打字稿 4.6.3

interface IProps<T> {
  data: T[];
}
export const YourComponent = <T,>(props: IProps<T>) => {}
Run Code Online (Sandbox Code Playgroud)

用法

const App = () => {
  const data : IDataProps[] = [
    {
      id:"1",
      label:"first banu"
    }
  ]
  return (
    <div className="App">
      <YourComponent data={data}/>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

  • 如何使用这个`YourComponent`? (6认同)

Tit*_*mir 13

您无法使用类型注释创建功能组件并使其成为通用组件.因此,这将T无法正常工作,您无法在变量级别定义它:

const CollapsableDataList : React.FunctionComponent<IProps<T>> = p => { /*...*/ } 
Run Code Online (Sandbox Code Playgroud)

但是,您可以跳过类型注释,并使函数通用并props明确键入.

import * as React from 'react';
import { render } from 'react-dom';

interface IProps<T> {
    collapsed: boolean;
    listOfData: T[];
    displayData: (data: T, index: number) => React.ReactNode;
}
const CollapsableDataList = <T extends object>(props: IProps<T> & { children?: ReactNode }) => {
    if (!props.collapsed) {
        return <span>total: {props.listOfData.length}</span>
    } else {
        return (
            <>
                {
                    props.listOfData.map(props.displayData)
                }
            </>
        )
    }
}


render(
    <CollapsableDataList
        collapsed={false}
        listOfData={[{a: 1, b: 2}, {a: 3, c: 4}]}
        displayData={(data, index) => (<span key={index}>{data.a + (data.b || 0)}</span>)}
    />,
    document.getElementById('root'),
)
Run Code Online (Sandbox Code Playgroud)

  • 您可以使用 PropsWithChildren&lt;IProps&lt;T&gt;&gt; 并获取接口交集的装备 (4认同)

Alf*_*ton 12

类型React.FC基本上是这样的:

<P = {}>(props: PropsWithChildren<P>, context?: any) => ReactElement | null
Run Code Online (Sandbox Code Playgroud)

所以而不是这个(这是不允许的):

const Example: React.FC<Props<P>> = (props) => {
  // return a React element or null
}
Run Code Online (Sandbox Code Playgroud)

你可以使用这个:

const Example = <P extends unknown>(props: PropsWithChildren<Props<P>>): ReactElement | null => {
  // return a React element or null
}
Run Code Online (Sandbox Code Playgroud)

例如:

const Example = <P extends unknown>({ value }: PropsWithChildren<{ value: P }>): ReactElement | null => {
  return <pre>{JSON.stringify(value)}</pre>
}
Run Code Online (Sandbox Code Playgroud)

或者,更严格地说,如果组件不使用childrenprop 并且不会返回null

const Example = <P>({ value }: { value: P }): ReactElement => {
  return <pre>{value}</pre>
}
Run Code Online (Sandbox Code Playgroud)

然后使用类型化组件作为 <Example<string> value="foo"/>


dmu*_*dro 7

在解决功能组件之前,我假设原始代码示例缺少 JSX 组件中的泛型,因为我没有看到它传递给IProps接口。IE。:

interface Ab {
  a: number;
  b: number;
}

...

// note passing the type <Ab> which will eventually make it to your IProps<T> interface and cascade the type for listOfData
return (
<CollapsableDataList<Ab>
  collapsed={false}
  listOfData={[{a: 1, b: 2}, {a: 3, c: 4}]}
  ...
/>
)
Run Code Online (Sandbox Code Playgroud)

好的,现在只需稍加努力,您实际上就可以拥有一个带有通用道具的功能组件。

你被困在使用“现代”语法,因为它使用了一个对你的通用情况没有用的赋值和箭头函数:

// using this syntax there is no way to pass generic props
const CollapsableDataList: React.FC<IProps> = ({ collapsed, listOfData }) => {
  // logic etc.
  return (
  // JSX output
  );
}
Run Code Online (Sandbox Code Playgroud)

让我们将变量赋值重写为一个很好的旧function

// we are now able to to write our function component with generics
function CollapsableDataList<T>({ collapsed, listOfData }: IProps<T> & { children?: React.ReactNode }): React.ReactElement {
  // logic etc.
  return (
  // JSX output
  );
}
Run Code Online (Sandbox Code Playgroud)

children如果组件不使用儿童撑起不是必需的解决方法,但我已经添加它来突出它需要手动重新输入一个事实,React.FC这样做对我们面前。


小智 6

type Props<T> = {
    active: T;
    list: T[];
    onChange: (tab: T) => void;
};

export const Tabs = <T,>({ active, list, onChange }: Props<T>): JSX.Element => {
    return (
        <>
            {list.map((tab) => (
                <Button onClick={() => onChange(tab)} active={tab === active}>
                    {tab} 
                </Button>
            ))}
        </>
    );
};
Run Code Online (Sandbox Code Playgroud)

  • 请注意 &lt;T,&gt; 内的悬空,它修复了编译器的错误并允许使用简单的泛型。 (8认同)
  • TypeScript 似乎认为 &lt;T&gt; 是使用 JSX 作为 React 组件的“.tsx”文件中名为“T”的组件的开始标签。为了超越转译器的默认解释到泛型的解释,可以像 @MatLipe 提到的那样引入逗号 &lt;T,&gt; (5认同)