En un componente basado en clases, puedo escribir fácilmente un código como este:
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'), ) En realidad, este componente CollapsableDataList debería ser un componente funcional porque no tiene estado, pero no puedo entender cómo escribir un componente de función y usar genéricos en accesorios, ¿algún consejo para mí?
No puede crear un componente funcional con una anotación de tipo y hacerlo genérico. Entonces, esto NO funcionará ya que T no está definido y no puede definirlo en el nivel de variable:
const CollapsableDataList : React.FunctionComponent<IProps<T>> = p => { /*...*/ } Sin embargo, puede omitir la anotación de tipo y hacer que la función sea genérica y que los props de tipo sean explícitos.
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'), )El tipo React.FC es esencialmente este:
<P = {}>(props: PropsWithChildren<P>, context?: any) => ReactElement | nullasí que en lugar de esto (que no está permitido):
const Example: React.FC<Props<P>> = (props) => { // return a React element or null }puedes usar esto:
const Example = <P extends unknown>(props: PropsWithChildren<Props<P>>): ReactElement | null => { // return a React element or null }Por ejemplo:
const Example = <P extends unknown>({ value }: PropsWithChildren<{ value: P }>): ReactElement | null => { return <pre>{JSON.stringify(value)}</pre> } O, de manera más estricta, si el componente no usa el apoyo de children y no devolverá null :
const Example = <P>({ value }: { value: P }): ReactElement => { return <pre>{value}</pre> } luego use el componente escrito como <Example<string> value="foo"/>
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> ))} </> ); };