리액트 관련 아티클을 읽거나 강의를 보다면 React.FC를 사용하는 것을 자주 목격할 수 있다.(특히, 오래된 아티클이나 강의)React.FC가 무엇인지 알아보고, 지양해야하는 이유에 대해서 알아보자
React.FC (React Functional Component)는 React에서 함수형 컴포넌트를 정의할 때 사용하는 TypeScript 타입입니다.
children 프롭스를 자동으로 포함한다
import React from 'react';
const Box: React.FC = ({ children }) => {
return (
<div style={{ border: '1px solid black', padding: '10px' }}>
{children}
</div>
);
};
const App: React.FC = () => {
return (
<Box>
<p>This is inside the Box component!</p>
</Box>
);
};
export default App;
위 예시에서 Box 컴포넌트는 React.FC를 사용하기 때문에 children을 자동으로 프롭스로 받을 수 있다. 따라서 App 컴포넌트에서 Box 안에
태그를 넣으면, 이 내용이 Box 컴포넌트 내부에 렌더링됩니다.
타입 안전성을 제공
import React from 'react';
interface ButtonProps {
label: string;
onClick: () => void;
}
const Button: React.FC<ButtonProps> = ({ label, onClick }) => {
return (
<button onClick={onClick}>
{label}
</button>
);
};
const App: React.FC = () => {
return (
<Button
label="Click Me"
onClick={() => alert('Button clicked!')}
/>
);
};
export default App;
이 예시에서 Button 컴포넌트는 label이라는 string 타입의 props와 onClick이라는 함수 타입의 프롭스를 받도록 React.FC<ButtonProps>를 사용해 정의했다. 이로 인해, App 컴포넌트에서 Button에 잘못된 타입의 프롭스를 전달하려고 하면 컴파일 타임에 오류가 발생한다.
그런데 왜 React.FC 사용을 지양해야 할까??
React.FC를 사용하면 children 프롭스가 자동으로 포함된다. 하지만 모든 컴포넌트가 children을 필요로 하는 것은 아니다. 따라서, 컴포넌트가 children을 사용하지 않는데도 불구하고 불필요하게 children 프롭스가 포함되는 것은 혼란을 야기할 수 있다는 것. 장점이 단점이 될 수 있다.
예를 들어, children이 필요 없는 단순한 버튼 컴포넌트의 경우:
interface ButtonProps {
label: string;
}
const Button: React.FC<ButtonProps> = ({ label }) => {
return <button>{label}</button>;
};
이런 경우에도 children이 기본으로 포함되기 때문에, 실수로 children을 전달하는 등의 문제가 발생할 수 있다.
React.FC는 컴포넌트의 반환 타입을 JSX.Element | null로 고정한다. 이는 유연성을 제한할 수 있다. 예를 들어, 일부 컴포넌트는 특정 조건에 따라 undefined를 반환할 수 있지만, React.FC를 사용하면 undefined는 허용되지 않는다.
const MyComponent: React.FC = () => {
if (someCondition) {
return null; // null은 허용
}
return undefined; // 오류 발생
};
TypeScript에서 defaultProps와 React.FC를 함께 사용할 때 문제가 발생할 수 있다. React.FC는 기본적으로 defaultProps를 잘 처리하지 않아서, 명시적인 타입 정의나 다른 방법이 필요합니다. 이 때문에 defaultProps를 사용하려면 번거로움이 생긴다.
interface MyComponentProps {
label?: string;
}
const MyComponent: React.FC<MyComponentProps> = ({ label = "Default" }) => {
return <div>{label}</div>;
};
MyComponent.defaultProps = {
label: "Default",
}; // 타입 문제 발생
많은 개발자들은 코드의 명확성과 유연성을 위해 React.FC를 사용하지 않고, 명시적으로 프롭스 타입과 반환 타입을 정의하는 것을 선호한다. 이렇게 하면 불필요한 children 프롭스를 포함하지 않으며, 반환 타입도 자유롭게 조정할 수 있다.
가장 일반적이고 간단한 방법인 props 옆에 타입을 정의해주자.
const R1: React.FC<RProps> = (props) => {}; // React.FC 사용
const R2 = (props: RProps) => {}; // 미사용