티스토리 뷰
[Typescript] React.FC를 사용하지 않아야 하는 이유(+ 리액트 컴포넌트 네임 스페이스 패턴)
blueprint-12 2023. 3. 26. 20:47FC를 쓰지 말아야 하는 이유
- children을 암시적으로 가지고 있다. (원치않는 에러를 발생시킬 수 있음)
- 제네릭을 지원하지 않는다.
- 네임 스페이스 패턴을 이용할 때 더 불편하다
- FC를 이용하면 코드가 더 길어진다.
- defaultProps와 정상적으로 동작하지 않는다.
결국 리액트 18 이상에서 없어졌다. 🤔 index.d.ts문서를 봐도 deprecated라고 명시되어 있다.
이 부분에 대해서 언급하기 이전에 함수 선언식과 함수 표현식(+화살표 함수) 복습
Function Declarations(함수 선언식) vs Function Expressions(함수 표현식)
우리가 기본적으로 함수를 작성할 때 사용하던 문법은 함수 선언식이다. function 키워드가 맨 앞에있는 친구
함수 표현식도 function 키워드를 사용하지만 변수에 함수를 할당하는 방식으로 생겼다. 뒤에도 언급하겠지만 화살표함수는 이런 function 키워드조차 사용하지 않는 함수 표현식의 간결한 version (ES6문법)이라고 생각하면 된다.
//함수 선언식
function 함수명 () {
//로직
}
//함수 표현식
let 함수명 = fucntion () {
//함수로직
}
let functionExp = function () {
return "hello";
}
2. 함수 선언식과 표현식의 차이점
- 함수 선언식은 호이스팅에 영향을 받지만, 함수 표현식은 호이스팅에 영향을 받지 않는다.
여기서 호이스팅이란 var 나 함수 선언식의 function 키워드로 선언된 변수와 함수가 최상단으로 끌어올려지는 현상을 말한다. 브라우저가 자바스크립트를 해석할 때 맨 위로 끌어올려지는 것
그렇기 때문에 가급적 함수와 변수를 코드 상단부에 선언하여 호이스팅으로 인한 스코프 꼬임현상을 방지하는 게 좋다.
3. 함수 표현식의 장점
호이스팅에 영향을 받지않는 특징 외에도 클로저로 사용하거나 콜백으로 사용할 수 있다(다른 함수의 인자로 넘김).
3-1. 함수 표현식의 클로저 활용
클로저는 함수를 실행하기 전에 해당 함수에 변수를 넘기고 싶을 때 사용된다.
클로저를 간단히 생각하면 내부 함수가 외부 함수에 접근하는 것이다.
function tabsHandler(index) {
return function tabClickEvent(event) {
// 바깥 함수인 tabsHandler 의 index 인자를 여기서 접근할 수 있다.
console.log(index); // 탭을 클릭할 때 마다 해당 탭의 index 값을 표시
};
}
var tabs = document.querySelectorAll('.tab');
var i;
for (i = 0; i < tabs.length; i += 1) {
tabs[i].onclick = tabsHandler(i);
}
for 반복문이 수행될 때 각 i값을 tabsHandler()에 넘기고, 클로저인 tabClickEvnet()에서 tabsHandler()의 인자 값 index를 접근할 수 있게 된다. 따라서 우리가 원하는 각 탭의 index에 접근할 수 있다.
3-2. 함수 표현식을 다른 함수의 인자값으로 넘기기
콜백 함수란 다른 함수의 인자로 전달된 함수를 의미한다. JS가 일급 객체로서 가지는 특징 중 하나이다.
jQuery를 사용할 때 많이 보던 문법으로 위와 아래의 코드 결과는 같다.
var logMessage = function () {
console.log('An anonymous function');
};
$(document).ready(logMessage); // 'An anonymous function'
자바스크립트 내장 API인 forEach()를 사용할 때도 콜백함수를 사용할 수 있다.
var arr = ["a","b","c"];
arr.forEach(function () {
});
+ AirBnb의 JS스타일 가이드에서도 함수 선언식보다는 함수 표현식을 지향하고 있다.
함수 표현식보다 단순하고 간결한 문법으로 함수를 만드는 방법이 바로 화살표 함수이다.
대안책이지만 제한이 있기 때문에 특징을 알고 있어야 한다.
4. 화살표 함수의 특징
- this나 super에 대한 바인딩이 없으며, methods로 사용될 수 없다.
- new.target 키워드가 없다. // new.target 속성은 함수 또는 생성자가 new 연산자를 사용하여 호출됐는지를 감지할 수 있다. new 연산자로 인스턴스화된 생성자 및 함수에서, new.target은 생성자 또는 함수 참조를 반환한다. 일반 호출에서는 undefined를 반환한다.
- Meta-property 'new.target' is only allowed in the body of a function declaration, function expression, or constructor. (17013) -new target 관련 오류 메세지-
- 일반적으로 스코프를 지정할 때 사용하는 call, apply, bind 메소드를 이용할 수 없다.
- 생성자(Constuctor)로 사용할 수 없다
- yield를 화살표 함수 내부에서 사용할 수 없다.
본론으로 돌아와 말하자면 우선, 리액트 컴포넌트가 선언식일 때는 FC를 사용하지 못한다.(FC는 말 그대로 함수에 대한 타입 정의이기 때문에 선언식에서는 사용할 수 없다.)
- 추가적으로 설명하자면 React.FC는 타입스크립트에서 제공하는 제네릭 타입 중 하나로, 함수형 컴포넌트가 반드시 반환하는 값을 타입으로 지정하기 위해 사용된다. 하지만 TS에서는 함수 선언문으로 작성된 함수에 타입을 명시할 수 없기 때문에 사용할 수 없다.
function MyComponent(props: PropsType) {
// ...
}
이유 3번째의 네임 스페이스 패턴?
네임 스페이스 패턴은 연관성 있는 컴포넌트에 대해 매우 많이 쓰이는 방법이다.
<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) => {
/*...*/
};
+ 네임 스페이스 패턴 추가 정리
"전역을 피하는 코딩 기법"
어플리케이션이나 라이브러리를 위해 전역 유효 범위에 많은 변수, 함수, 객체 등으로 어지럽히지 않도록 하기 위해 전역 객체를 하나만 만들고, 모든 기능을 이 객체에 추가하는 패턴을 네임스페이스 패턴이라 한다.
스크립트가 실행되는 환경을 항상 일정하게 만들어 관리하기 쉽게 만드는 일이다. 단순히 기능을 한데 묶기 위해 사용하는 객체를 네임스페이스 라고 한다. 가장 대표적인 네임스페이스 패턴은 객체 리터럴 네임스페이싱 방식이다.
장점: 전역 변수의 개수를 줄이고, 같은 이름 충돌을 방지하며, 변수명이 불필요하게 길어짐을 방지
단점: 각 프로퍼티(변수, 객체 등) 상위 객체명을 계속 붙여야 하므로 소스 양은 늘어난다.
객체 리터럴 네임스페이싱 예시
MYAPP.modules = {};
MYAPP.modules.module1 = {};
MYAPP.modules.module1.data = {a:1, b:2};
리액트 컴포넌트에서 네임 스페이스 패턴을 적용한 예시와 관련 설명은 해당 링크를 참고하면 된다.
이유 3번째의 코드가 더 길어지는 단점?
const C1: React.FC<CProps> = (props) => {};
const C2 = (props: CProps) => {};
결론: FC를 사용하지 않는다면 대안책은?
1번째 옵션
const PrintName2 = ({ prop1, prop2 }: Props): JSX.Element => { /** */}
2번째 옵션
interface FunctionComponent<P = {}> {
(props: PropsWithChildren<P>, context?: any): ReactElement | null;
propTypes?: WeakValidationMap<P>;
contextTypes?: ValidationMap<any>;
defaultProps?: Partial<P>;
displayName?: string;
}
더 자세한 내용은 stack overflow의 본문을 참고하면 좋다.
🚩 all ref:
'Frontend > TypeScript' 카테고리의 다른 글
[webpack] babel, TS, webpack 세팅하기 1(+ proxy로 CORS에러 해결하기) (0) | 2023.05.17 |
---|---|
[TypeScript] 다형성(polymorphism)과 제네릭 (0) | 2023.05.02 |
[TypeScript | hooks] useRef 사용법( ref 객체 타입) (0) | 2023.03.05 |
[TypeScript | styled-components] 스타일드 컴포넌트 사용하기 (0) | 2023.03.05 |
[TypeScript] 타입스크립트 자동완성과 DT, TS (0) | 2023.01.14 |
- Total
- Today
- Yesterday
- D 플래그
- float 레이아웃
- 항해99추천비추천
- nvm경로 오류
- 타입스크립트 DT
- 형제 요소 선택자
- ~ ^
- fs모듈 넥스트
- getServerSideProps
- 항해99프론트후기
- && 셸 명령어
- is()
- text input pattern
- reactAPI
- 부트캠프항해
- tilde caret
- 프리렌더링확인법
- grid flex
- 원티드 프리온보딩 프론트엔드 챌린지 3일차
- 틸드와 캐럿
- 원티드 FE 프리온보딩 챌린지
- Prittier
- aspect-ratio
- 원티드 프리온보딩 FE 챌린지
- 타입스크립트 장점
- nvm 설치순서
- 항해99프론트
- 프리온보딩 프론트엔드 챌린지 3월
- 원티드 3월 프론트엔드 챌린지
- getStaticPaths
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |