참조사이트 : https://fettblog.eu/typescript-react-component-patterns/
React와 Typescript를 같이 쓰면서 편한점이 매우 많았습니다. 앞으로도 계속 사용할 것 같아서 그 사이에 있던 불편함, 의문점에 궁금즘을 가지다가 지나가면서 보았던 article을 옮겨보고자 합니다.
특히 React.FC
에 대해서 작성된 글입니다.
React에서 component를 정의할 때는 2가지 방법이 있습니다.
Component
로 부터 상속받은 class로 정의하는 방법JSX
를 return하는 function으로 정의하는 방법React는 typescript로 작성되지 않았기 때문에 Definitely Typed인 @types/react
package를 사용합니다.
이 package에서 FC
라고 불리는 fucntion component type을 제공합니다.
import React, { FC } from "react";
type GreetingProps = {
name: string;
}
const Greeting:FC<GreetingProps> = ({ name }) => {
// name is string!
return <h1>Hello {name}</h1>
};
FC
interface는 아래와 같습니다.
interface FunctionComponent<P = {}> {
(props: PropsWithChildren<P>, context?: any)
: ReactElement<any, any> | null;
propTypes?: WeakValidationMap<P>;
contextTypes?: ValidationMap<any>;
defaultProps?: Partial<P>;
displayName?: string;
}
function 또한 type을 지정하여 사용하는 것인데 왜 저자는 FC
type을 사용하지 않는다고 하는 걸까?
React.FC
는 function에 type을 지정합니다.
function component
그 자체로 type을 지정합니다.
Function type은 일반적인 function에 적용하기 매우 어렵습니다.
아래와 같은 function은 type을 어떻게 지정해야 할까요?
function Greeting({ name }) {
return <h1>Hello {name}</h1>
}
아래와 같이 const에 할당하여 anonymous function으로 사용할 수 있습니다.
interface GreetingProps {
name: string;
}
const Greeting:FC<GreetingProps> = function({ name }) {
return <h1>Hello {name}</h1>
}
또는 array function으로도 사용가능합니다.
interface GreetingProps {
name: string;
}
const Greeting:FC<GreetingProps> = ({ name }) => {
return <h1>Hello {name}</h1>
}
위와 같은 function에 대해 type은 지정가능합니다.
그러나 simple, regular function은 완전히 배제됩니다.
만약 function에 type을 지정하지 않고 props에만 type을 지정한다면 모든 형태의 함수를 사용하는 목표를 달성할 수 있습니다.
interface GreetingProps {
name: string;
}
// ✅
function Greeting({ name }: GreetingProps) {
return <h1>Hello {name}</h1>
}
그렇죠. arrow function을 포함해 다양한 형태의 오래되거나, 지루하지만 여전히 작동하거나, 또는 간단한 이름인 function을 작성하는 것이 가능합니다.
React.FC
는 암묵적으로 children
prop을 내장하고 있습니다.
export const Greeting:FC<GreetingProps> = ({ name }) => {
// name is string!
return <h1>Hello {name}</h1>
};
// use it in the app
const App = () => <>
<Greeting name="Stefan">
<span>{"I can set this element but it doesn't do anything"}</span>
</Greeting>
</>
위와 같이 children
prop을 지정하지 않더라도 위 코드는 오류없이 동작이 되는 것을 볼 수 있습니다.
function Greeting({ name }: GreetingProps) {
return <h1>Hello {name}</h1>
}
const App = () => <>
<Greeting name="Stefan">
{/* The next line throws errors at me! 💥*/}
<span>{"I can set this element but it doesn't do anything"}</span>
</Greeting>
</>
React.FC
를 대신에 simple props 만 정의한다면 오류를 throw할 것입니다.
children
prop을 다루는 것이 typescript의 장점은 아닙니다. 그러나 적어도 children
이 암묵적으로 선언되어 사용되는 것보단 명시적으로 사용하는 것이 좋을 것입니다.
Preact를 사용하지 않고 있다면 사용해야만 합니다.
이 preact/compat
package는 여러분이 React 생태계와 호환되는지 확인해줄 것입니다.
그리고 최대 100KB production size을 줄일 수 있고, 독립적인 라이브러리를 사용할 수 있습니다.
Preact: Google의 Jason Miller가 개발한 라이브러리로, 아주 작은 크기의 코드(3KB, 코드 압축 및 gzip 압축 기준)로 React(또는 유사한 프레임워크인 Mithril)의 핵심 가치를 구현하기 위해 2015년에 시작된 프로젝트이다.
React의 작고 가벼운 버전을 원하면서 기존 생태계를 활용하려는 개발자에게 많은 관심을 이끌어 냈다(2019년 8월을 기준으로 GitHub Stars의 수치가 23,3K이다).
Preact와 React 차이 : https://preactjs.com/guide/v8/differences-to-react/
Preact
는 typescript로 작성되었습니다.(JSDoc annotaion포함)
따로 definitly types가 필요없습니다.
그래서 @types/react
와 더이상 호환되지 않을 것입니다.
React.FC
또한 마찬가지이기 때문에 기존 코드에 있다면 리팩터링 해야만 됩니다.
class기반 React에서 prop의 default값을 지정해주기 위해 defaultProps
가 있습니다.
function componentns에서 지금은 아래와 같이 지정하여 사용합니다.
type LoginMsgProps = { name?: string; }; function LoginMsg({ name = "Guest" }: LoginMsgProps) { return <p>Logged in as {name}</p>; }
그렇지만 defaultProps
설정이 여전히 필요한 상황에 처할 수 있습니다.
typescript는 defaultProps
를 이해하는 메커니즘이 있으며 여러분이 설정한 값을 기반으로 기본값을 설정할 수 있습니다.
하지만 React.FC
는 defaultProps
를 연결을 끊고 props으로 들어온 기본값으로 사용합니다.
export const Greeting:FC<GreetingProps> = ({ name }) => {
// name is string!
return <h1>Hello {name}</h1>
};
Greeting.defaultProps = {
name: "World"
};
const App = () => <>
{/* Big boom 💥*/}
<Greeting />
</>
React.FC
사용하지 않는다면 오류없이 사용가능합니다.
export const Greeting = ({ name }: GreetingProps) => {
// name is string!
return <h1>Hello {name}</h1>
};
Greeting.defaultProps = {
name: "World"
};
const App = () => <>
{/* Yes! ✅ */}
<Greeting />
</>
앞으로 어떤 변화가 있을 지 모르는 상황에서 이 React.FC
type은 바뀔 가능성이 없다고는 말할 수 없습니다.
hook이 막 도입되던 시기에는 많은 state들이 function component에서 관리되었습니다. 이것은 SFC
type인 현재 사용되는 FC
로 반영되었습니다.
그렇다면 미래에 변화에 따라 또다른 형태로 바뀔 수 있다고 우리는 느꼈을 겁니다.
대신 function 자체 type이 아닌 props type만을 지정하여 사용한다면 미래에 어떤 형태가 되어도 영향도가 없을 것입니다.
저자 마무리 :
React.FC
를 사용하지 않는 몇가지 이유에 대해서 설명하였습니다.하지만
React.FC
가 workflow에 적합하고 그리고 그것을 사용하는 것이 전적으로 좋다고 생각합니다. 과거에도 많이 사용해왔고 무엇보다 잘 작동합니다.
따라서 코딩 스타일을 변경해야한다는 부담을 느끼지 마시길 바랍니다.하지만 only typed props를 통한 function component가 훨씬 간단하고 javascript에 더 가깝다는 것을 알 수 있습니다. 그리고 이것이 여러분의 코딩 스타일에 더 적합할 것입니다.
function component를 사용하면서 FC
를 통해 IDE의 도움으로 return value나 그 외 주요 편의 기능을 많이 이용해왔는데 위와 같은 글을 보면서 사용하게 될 때 한 번 더 생각을 할 것 같습니다.
뭐가 더 좋은지 더 나쁜지는 없는 것 같습니다.
자신의 코딩 스타일에 따라 사용하면 됩니다.
감사합니다