- Published on
타입스크립트 리액트 컴퍼넌트
- Authors

- Name
- dwook
Table of Contents
함수형 컴퍼넌트를 작성하는 2가지 방식
1. function 키워드(리액트메뉴얼, 유명개발자)
- function 키워드를 사용하면 childrend이 없기 때문에
children?: React.ReactNode작성해줘야 한다. - children이 필요없는 컴퍼넌트도 존재한다!
- function 키워드를 사용한다면,
컴퍼넌트.defaultProps가 제대로 동작.
import React from 'react';
type GreetingsProps = {
name: string;
mark: string; // ?처리 안해도됨.
};
function Greetings({ name, mark }: GreetingsProps) {
return (
<div>
Hello, {name} {mark}
</div>
);
}
Greetings.defaultProps = {
mark: '!'
};
export default Greetings;
2. 화살표함수(React.FC)
- 장점
- children props가 자동으로 설정되어서
children?: React.ReactNode작성하지 않아도 된다. - 자동완성 프로퍼티
- children props가 자동으로 설정되어서
- 단점
컴퍼넌트.defaultProps가 제대로 동작하지 않아서 타입정의에 ?를 사용해야한다.- 그런데, ?를 사용하면 undefined 타입이 추가되는 상황이 발생!
React.FC를 사용해서 컴퍼넌트를 선언하면, 컴퍼너트.defaultProps가 먹통
import React from 'react';
type GreetingsProps = {
name: string;
mark?: string; // ?를 붙이면, string 또는 undefined
array?: string[];
};
const Greetings: React.FC<GreetingsProps> = ({ name, mark, array }) => {
if(!array) return null; // ?처리를 해서 undefined가 될 수 있기 때문에 처리 필요
return (
<div>
Hello, {name} {mark}
</div>
);
}
Greetings.defaultProps = {
mark: '!'
};
export default Greetings;
// 해결방법. default 값을 파라미터 위치에 넣어주기.
import React from 'react';
type GreetingsProps = {
name: string;
mark?: string;
};
const Greetings: React.FC<GreetingsProps> = ({ name, mark = '!' }) => {
// 이렇게 처리해야 제대로 작동. mark 값이 undefined면, 기본값으로 느낌표.
return (
<div>
Hello, {name} {mark}
</div>
);
}
export default Greetings;
컴퍼넌트 작성
import React, { useCallback } from 'react';
// Basic Object 타이핑
const Heading = (props: { title: string }) => <h2>{props.title}</h2>
const Heading = ({ title }: { title: string }) => <h2>{title}</h2>
const Box = ({ children }: { children: React.ReactNode }) => <div>{children}</div>
const Box: React.FunctionComponent = ({ children }) => <div>{children}</div>
const List: React.FunctionComponent<{
items: string[];
onClick?: (item: string) => void;
}> = ({ items, onClick }) => (
<ul>
{items.map((item, index) => (
<li key={index} onClick={() => onClick?.(item)}>{item}</li>
))}
</ul>
)
function App() {
const onListClick = useCallback((item: string) => {
alert(item)
}, []);
return (
<div>
<Heading title="Introduciton" />
<Box>
Hello there!
</Box>
<List items={["one", "two", "three"]} />
</div>
)
}
Props 타입 작성
type Props = {
message: string;
count: number;
disabled: boolean;
names: string[]; // 타입의 배열
status: "waiting" | "success"; // 정확한 문자열 값을 지정
optional?: OptionalType; // optional
/* PREFERRED object */
obj3: { // 프로퍼티가 존재하는 객체 표현
id: string;
title: string;
};
/* COMMON object */
objArr: { // 객체의 배열
id: string;
title: string;
}[];
/* DICT object */
dict1: { // 동일한 유형의 프로퍼티 속성 수에 관계 없는 dict 객체
[key: string]: MyTypeHere;
};
dict2: Record<string, MyTypeHere>; // dict1와 동일
/* NOT COMMON object */
obj: object; // 프로퍼티를 사용하지 않는 객체 표현.
obj2: {}; // `object`와 거의 비슷, `Object`와 정확히 같음.
/* VERY COMMON function */
onClick: () => void; // 인자를 취하거나 반환하지 않는 function
onChange: (id: number) => void; // 명명된 파라미터가 있는 function
onClick(event: React.MouseEvent<HTMLButtonElement>): void;
/* NOT RECOMMENDED function */
onSomething: Function;
};
Pros로 컴퍼넌트를 전달하는 경우
interface Props {
children0: React.ReactNode; // 최고!
children1: JSX.Element; // 나쁨. 배열을 고려하지 않음.
children2: JSX.Element | JSX.Element[]; // 나쁨. 문자열을 허용하지 않음.
children3: React.ReactChildren; // 적절하지 않은 타입. 유틸리티.
children4: React.ReactChild[]; // 배열을 고려하긴함.
functionChildren: (name: string) => React.ReactNode; // 자식을 렌더링하는 함수 타입으로 추천!
style?: React.CSSProperties; // 스타일 프로퍼티를 전달
onChange?: React.FormEventHandler<HTMLInputElement>; // form 이벤트. 제너릭 매개변수는 vent.target 타입
props: Props & React.ComponentPropsWithoutRef<"button">;
// button의 모든 props을 가장하고 해당 ref를 명시적으로 전달하지 않음
props2: Props & React.ComponentPropsWithRef<MyButtonWithForwardRef>;
// MyButtonForwardedRef의 모든 props를 가장하고 해당 ref를 명시적으로 전달
}
children
type Props = {
children: React.ReactNode;
children: React.ReactChild;
children: JSX.Element;
};
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
type ReactText = string | number;
type ReactChild = ReactElement | ReactText;
namespace JSX {
interface Element extends React.ReactElement<any, any> { }
}
React.ReactNode- 클래스형 컴포넌트 render()에서 반환(return) 타입
- null 허용
React.ReactChild- 함수형 컴포넌트 반환(return) 타입
- React.ReactChild는 ReactElement와 string, number를 상속(extends)받음.
- null 허용하지 않음
JSX.Element(ReactElement)- React.createElement() 함수의 반환(return) 타입
- null 허용하지 않음
왜 클래스형 컴포넌트와 함수형 컴포넌트를 분리했을까?
- 사실 이 둘을 혼용을 해서 사용해도 큰 문제는 생기지 않을 것
- 가장 큰 차이점은 반환(return) 타입으로 null을 허용하는지 유무
JSX.Element(ReactElement) ⊂ ReactChild ⊂ ReactNode
- ReactNode, ReactChild, ReactElement 사용시 주의사항
- 모두 children으로
{}를 받을 수 없음. 에러발생 시점이 다르다! - ReactNode는 런타임에서 에러발생.
- ReactChild, JSX.Element(ReactElement)는 컴파일타임에서 에러발생
- 모두 children으로
이벤트 타입
function App() {
const onChangeFormHandler = (e: React.ChangeEvent<HTMLInputElement>) => {}
const onKeyboardHandler = (e: React.KeyboardEvent<HTMLInputElement>) => {}
const onClickHandler = (e: React.MouseEvent<HTMLButtonElement>) => {}
const onSubmitHandler = (e: React.FormEvent<HTMLFormElement>) => {}
return (
<>
<form onSubmit={onSubmitHandler}>
<input onChange={onChangeFormHandler} />
<input onKeyDown={onKeyboardHandler} />
<button onClick={onClickHandler} />
</form>
</>
);
}
export default App;
Detailed HTML props
- ?? 연산자는 왼쪽 피연산자가
null또는undefined일 때만 오른쪽 피연산자를 반환 - || 연산자는 왼쪽 피연산자가 falsy 값에 해당할 경우, 오른쪽 피연산자를 반환
const Button: React.FunctionComponent<
React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement
> & {
title?: string; // 새로 추가한 커스텀 props
}
> = ({ title, children, style, ...rest }) => (
<button
{...rest}
style={{
...style,
backgroundColor: "red",
color: "white",
fontSize: "xx-large",
}}
>
{title ?? children}
</button>
);
Generic 컴퍼넌트
function UL<T>({
items,
render,
itemClick,
}: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>,HTMLUListElement>
& {
items: T[];
render: (item: T) => React.ReactNode;
itemClick: (item: T) => void;
}) {
return (
<ul>
{items.map((item, index) => (
<li onClick={() => itemClick(item)} key={index}>
{render(item)}
</li>
))}
</ul>
);
}
function UL<T>({
items,
render,
children,
}: React.PropsWithChildren<React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>,HTMLUListElement>
& {
items: T[];
render: (item: T) => React.ReactNode;
}>) {
return (
<ul>
{items.map((item, index) => (
<li key={index}>
{render(item)}
</li>
))}
</ul>
);
}
function App() {
return <>
<UL
items={todos}
itemClick={(item) => alert(item.id)}
render={(todo) => (
<>
{todo.text}
<button onClick={() => removeTodo(todo.id)}>Remove</button>
</>
)}
/>
</>
}
참조링크