React + Typescript 에서 FC 를 사용하면 안되는 이유
먼저 React17 이하에서는 아래와 같이 React.FC 사용이 가능하며 React18 이상부터는 없어졌다고 한다.
참고
그러나 FC 를 사용하는건 좋은 방법이 아니며 CRA 에서는 여러가지의 이유로 FC 를 없애야 한다고 이슈가 올라왔었고 반영되었으며 이유는 아래 설명을 참고하자.
- children 을 암시적으로 가지고 있다.
- FC 를 이용한 컴포넌트 props 는 type 이 ReactNode 인 Children 을 암묵적으로 가지게 되며 이는 꼭 타입스크립트에 한정하지 않더라도 안티패턴이라고 한다. 왜냐하면 children 이라는 암묵적이었던 키워드는 자식노드가 필요한지 아닌지의 의도를 명확하게 드러낼 수 없다는, 타입의 관점에서는 꽤 크리티컬한 문제를 갖고 있었다. (타입에 자식 노드가 꼭 필요한 컴포넌트에 자식 노드를 넣지 않았을 경우 혹은 그 반대의 경우를 타입으로 잡아내지 못한다는 단점이 존재
즉, 암묵적으로 선언됐던 children 을 명시적으로 컴포넌트에 맞게 선언을 하자는 취지라고 한다.
) - 예시
- AS-IS (react 를 업데이트하면 하기 주석의 에러가 표시)
const Example: React.FC = ({ children }) => <div>{children}</div>; // "TS2339: Property 'children' does not exist on type '{}'."
- TO-BE (자식 컴포넌트가 꼭 필요하다는 의도를 타입으로 정의)
type Props = { children: React.ReactNode; // children?: React.ReactNode; 자식노드가 옵셔널한경우 }; const Example: React.FC<Props> = ({ children }) => <div>{children}</div>;
- AS-IS (react 를 업데이트하면 하기 주석의 에러가 표시)
- FC 를 이용한 컴포넌트 props 는 type 이 ReactNode 인 Children 을 암묵적으로 가지게 되며 이는 꼭 타입스크립트에 한정하지 않더라도 안티패턴이라고 한다. 왜냐하면 children 이라는 암묵적이었던 키워드는 자식노드가 필요한지 아닌지의 의도를 명확하게 드러낼 수 없다는, 타입의 관점에서는 꽤 크리티컬한 문제를 갖고 있었다. (타입에 자식 노드가 꼭 필요한 컴포넌트에 자식 노드를 넣지 않았을 경우 혹은 그 반대의 경우를 타입으로 잡아내지 못한다는 단점이 존재
- 제네릭을 지원하지 않는다.
- 하기 제네릭 컴포넌트를 작성한 내용을 보자
// 제네릭 컴포넌트 작성 type GenericComponentProps<T> = { prop: T; callback: (t: T) => void; }; const GenericComponent = <T>(props: GenericComponentProps<T>) => { /*...*/ };
// 허용 X (T 를 넘겨줄 방법이 없기 때문에 FC 에서 허용되지 않는다) const GenericComponent: React.FC</* ??? */> = <T>(props: GenericComponentProps<T>) => {/*...*/}
- 하기 제네릭 컴포넌트를 작성한 내용을 보자
- 네임스페이스 패턴을 이용할 때 불편하다.
- 하기 코드와 같이 연관성 있는 컴포넌트에 대해 네임스페이스 패턴을 적용하는 것은 매우 많이 쓰이는 방법이다.
<Select> <Select.Item /> </Select>
- FC 를 사용하고도 상기 패턴을 적용할 수 있지만 많이 불편해진다.
// FC를 사용할 때 const Select: React.FC<SelectProps> & { Item: React.FC<ItemProps> } = ( props ) => { /* ... */ }; Select.Item = (props) => { /*...*/ };
// FC를 사용하지 않을 때 const Select = (props: SelectProps) => { /* ... */ }; Select.Item = (props: ItemProps) => { /*...*/ };
- 하기 코드와 같이 연관성 있는 컴포넌트에 대해 네임스페이스 패턴을 적용하는 것은 매우 많이 쓰이는 방법이다.
- FC 를 사용하면 코드가 더 길어진다.
const C1: React.FC<CProps> = (props) => {}; // FC 사용 O const C2 = (props: CProps) => {}; // FC 사용 X
참고
- https://emewjin.github.io/
- https://blog.shiren.dev/