programing

기능적인 컴포넌트에서 React의 소품에서 제네릭스를 사용하는 방법

easyjava 2023. 2. 23. 23:07
반응형

기능적인 컴포넌트에서 React의 소품에서 제네릭스를 사용하는 방법

클래스 베이스의 컴포넌트에서는, 다음과 같은 코드를 간단하게 쓸 수 있습니다.

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'),
)

사실 이거CollapsableDataList스테이트리스이기 때문에 컴포넌트는 기능 컴포넌트여야 하는데, 함수 컴포넌트를 쓰는 방법과 소품에서 범용 컴포넌트를 사용하는 방법을 알 수 없습니다. 조언해 주실 수 있나요?

유형 주석을 사용하여 기능 구성요소를 작성하고 일반화할 수 없습니다.따라서 이 방법은T정의되어 있지 않기 때문에, 가변 레벨로 정의할 수 없습니다.

const CollapsableDataList : React.FunctionComponent<IProps<T>> = p => { /*...*/ } 

그러나 유형 주석을 건너뛰고 함수를 일반 및 유형으로 만들 수 있습니다.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'),
)

종류React.FC기본적으로 다음과 같습니다.

<P = {}>(props: PropsWithChildren<P>, context?: any) => ReactElement | null

따라서 이 대신(허용되지 않음):

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

다음을 사용할 수 있습니다.

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

예를 들어 다음과 같습니다.

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

또는 보다 엄밀하게는 컴포넌트가 다음 컴포넌트를 사용하지 않는 경우children버팀목이 되어 돌아오지 않다null:

const Example = <P>({ value }: { value: P }): ReactElement => {
  return <pre>{value}</pre>
}

다음으로 입력된 컴포넌트를 사용합니다.<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>
            ))}
        </>
    );
};

기능 컴포넌트를 어드레싱하기 전에, 원래의 코드 예는 JSX 컴포넌트에 범용이 없는 것으로 생각됩니다.이것은, JSX 컴포넌트에 전달되지 않았기 때문입니다.IProps인터페이스입니다.I. e.:

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}]}
  ...
/>
)

자, 이제 조금만 노력하면 범용 소품을 갖춘 기능적인 컴포넌트를 실제로 얻을 수 있습니다.

일반적인 경우 사용할 수 없는 할당 및 화살표 기능을 사용하므로 '현대' 구문을 사용할 수밖에 없습니다.

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

변수 할당을 good old로 다시 씁니다.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
  );
}

children컴포넌트가자체프롭을사용하지않는경우회피책이필요하지는않습니다.다만, 수동으로 재입력할 필요가 있는 것을 강조하기 위해서 추가했습니다.React.FC아까도 해줬는데

리액트 18, 타입 스크립트 4.6.3

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

#1을 보충합니다.

컴포넌트를 Function Component로 내보내고 eslint displayName 오류를 전달할 경우.

넌 할 수 있어

const yourComponentWithLowerCase: <T>(props: PropsWithChildren<Props<T>>) => ReactElement | null = (props) => {
  // code
}

export const YourComponentWithUpperCase = yourComponentWithLowerCase;
(YourComponentWithUpperCase as FunctionComponent).displayName = 'something'


답변은 소품 및 함수의 반환 유형을 적절하게 정의하기 때문에 좋은 예입니다.

대안으로 화살표 함수 대신 함수로 정의할 수 있습니다.이 때문에 프로펠러 타입을 확장하여 이것이 리액트 컴포넌트가 아님을 TS 컴파일러에 암시할 필요가 없어집니다.

export function CollapsableDataList<T>(
  props: PropsWithChildren<IProps<T>>
): ReturnType<FunctionComponent<IProps<T>>> {
  // ...
}

언급URL : https://stackoverflow.com/questions/53958028/how-to-use-generics-in-props-in-react-in-a-functional-component

반응형