React
에서TypeScript
를 적용하여 타입을 지정해야 하는데,
일반적인JavaScript
에 타입 지정하는 방법에 비해React
는 컴포넌트 타입, 이벤트 핸들링, 상태 관리, 고차 컴포넌트 등 다양한 타입 지정 방법을 정리하면 좋을 것 같아서 작성하게 됐습니다.
React.FC
Functional Component를 나타내는 타입으로 함수형 컴포넌트의 속성을 정의할 수 있고 TypeScript와 함께 사용할 때 타입 안정성을 제공
- Props 타입 정의 : 컴포넌트의 props에 대한 타입을 명시적으로 정의할 수 있음
- Children Prop 자동 포함 :
React.FC
는 자동적으로children
prop을 포함- PropTypes 및 기본값 지원 : PropTypes의 검증과 기본값 설정을 할 수 있음
const MyComponent: React.FC = () => {
return <div>내용</div>;
};
interface MyComponentProps {
title: string;
}
const MyComponent: React.FC<MyComponentProps> = ({ title }) => {
return <div>{title}</div>
}
const [count, setCount] = useState<number>(0);
const [value, setValue] = useState<string>('');
const [value, setValue] = useState<string | number>(0);
const [isTrue, setIsTrue] = useState<boolean>(false);
const [arr, setArr] = useState<string[]>([]);
const myRef = useRef<HTMLDivElement>(null); // div Element
const myRef = useRef<HTMLInputElement>(null); // input Element
const myRef = useRef<HTMLButtonElement>(null); // button Element
import React, { useContext } from 'react';
interface AppContextInterface {
name: string;
author: string;
}
const AppContext = React.createContext<AppContextInterface | null>(null);
const DisplayComponent: React.FC = () => {
const context = useContext(AppContext);
return (
<div>
<p>앱 이름: {context?.name}</p>
<p>작성자: {context?.author}</p>
</div>
);
};
import React, { useReducer } from 'react';
interface StateType {
count: number;
}
type ActionType = { type: 'increment' } | { type: 'decrement' };
function reducer(state: StateType, action: ActionType): StateType {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
const CounterComponent: React.FC = () => {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p>현재 카운트: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>증가</button>
<button onClick={() => dispatch({ type: 'decrement' })}>감소</button>
</div>
);
};
import React, { useState, useEffect } from 'react';
function useCustomHook(defaultValue: number) {
const [value, setValue] = useState<number>(defaultValue);
useEffect(() => {
// 부수 효과나 추가 로직
}, [value]);
return [value, setValue] as const; // const assertion으로 타입을 더 명확히 할 수 있음
}
const CustomHookComponent: React.FC = () => {
const [count, setCount] = useCustomHook(0);
return (
<div>
<p>현재 카운트: {count}</p>
<button onClick={() => setCount(count + 1)}>증가</button>
</div>
);
};
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
// 로직 구현
};
return <>
<button onClick={handleClick}>클릭</button>
</>
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
// 로직 구현
};
return <>
<input type="text" onChange={handleChange} />
</>
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
// 로직 구현
};
// 마우스 이벤트
const handleMouseOver = (e: React.MouseEvent<HTMLDivElement>) => {
// 로직 구현
};
// 키보드 이벤트
const handleKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
// 로직 구현
};
interface MyComponentProps {
content: React.ReactNode; // ReactNode 타입의 프롭스
}
const MyComponent: React.FC<MyComponentProps> = ({ content }) => {
return <div>{content}</div>;
};
interface MyComponentProps {
Component: React.ComponentType; // 함수 또는 클래스 컴포넌트
}
const MyComponent: React.FC<MyComponentProps> = ({ Component }) => {
return <Component />;
};
interface MyComponentProps {
onClick: () => void; // 매개변수 없고 반환 값 없는 함수
}
const MyComponent: React.FC<MyComponentProps> = ({ onClick }) => {
return <button onClick={onClick}>클릭</button>;
};
interface MyComponentProps {
onSubmit: (value: string) => void; // 문자열 매개변수를 받는 함수
}
const MyComponent: React.FC<MyComponentProps> = ({ onSubmit }) => {
const handleSubmit = () => {
onSubmit('Some value');
};
return <button onClick={handleSubmit}>제출</button>;
};
interface MyComponentProps {
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void; // 입력 변경 이벤트 핸들러
}
const MyComponent: React.FC<MyComponentProps> = ({ onChange }) => {
return <input type="text" onChange={onChange} />;
};
interface MyComponentProps<T> {
onCustomAction: (item: T) => void; // 제네릭 타입을 매개변수로 받는 함수
}
const MyComponent: React.FC<MyComponentProps<number>> = ({ onCustomAction }) => {
return <button onClick={() => onCustomAction(5)}>액션</button>;
};
interface MyComponentProps {
setCount: React.Dispatch<React.SetStateAction<number>>; // 숫자 상태를 업데이트하는 함수
}
const MyComponent: React.FC<MyComponentProps> = ({ setCount }) => {
return <button onClick={() => setCount(prevCount => prevCount + 1)}>증가</button>;
};
interface MyState {
name: string;
age: number;
}
interface MyComponentProps {
setState: React.Dispatch<React.SetStateAction<MyState>>; // 객체 상태를 업데이트하는 함수
}
const MyComponent: React.FC<MyComponentProps> = ({ setState }) => {
return <button onClick={() => setState(prevState => ({ ...prevState, age: prevState.age + 1 }))}>나이 증가</button>;
};
interface MyComponentProps {
setCount?: React.Dispatch<React.SetStateAction<number>>; // 선택적 상태 변환 함수
}
const MyComponent: React.FC<MyComponentProps> = ({ setCount }) => {
return setCount ? <button onClick={() => setCount(prevCount => prevCount + 1)}>증가</button> : null;
};
// counterSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface CounterState {
value: number;
}
const initialState: CounterState = {
value: 0
};
const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: state => {
state.value += 1;
},
addAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
}
}
});
export const { increment, addAmount } = counterSlice.actions;
export default counterSlice.reducer;
// store.ts
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';
const store = configureStore({
reducer: {
counter: counterReducer
}
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export default store;
// CounterComponent.tsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement, addAmount } from './counterSlice';
import { RootState } from './store';
export const CounterComponent: React.FC = () => {
const count = useSelector((state: RootState) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<p>현재 카운트: {count}</p>
<button onClick={() => dispatch(increment())}>증가</button>
<button onClick={() => dispatch(decrement())}>감소</button>
<button onClick={() => dispatch(addAmount(5))}>5 더하기</button>
</div>
);
};
// atoms.ts
import { atom } from 'recoil';
export const countState = atom({
key: 'countState', // 고유한 키
default: 0, // 초기값
});
// selectors.ts
import { selector } from 'recoil';
import { countState } from './atoms';
export const doubledCountState = selector({
key: 'doubledCountState',
get: ({ get }) => {
const count = get(countState);
return count * 2;
},
});
// CounterComponent.tsx
import React from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { countState, doubledCountState } from './selectors';
export const CounterComponent: React.FC = () => {
const [count, setCount] = useRecoilState(countState);
const doubledCount = useRecoilValue(doubledCountState);
return (
<div>
<p>현재 카운트: {count}</p>
<p>두 배 카운트: {doubledCount}</p>
<button onClick={() => setCount(count + 1)}>증가</button>
<button onClick={() => setCount(count - 1)}>감소</button>
</div>
);
};