
지금까지 리액트는 자바스크립트로만 다뤘다. 타입스크립트를 다루고 싶었지만 문법만 공부하는 것은 재미가 없어 간단한 토이프로젝트들을 진행하면서 타입스크립트를 정리하려고 한다.
자바스크립트에서 타입스크립트로 넘어오면서 느낀점
자바스크립트에서 단순히 props로 값을 전달 할 때 발생할 수 있는 잠재적인 오류들이 있다. 어떤 이유로 예상되는 값들이 전달되지 않거나 props의 필드 값들을 미리 적어놓지 않으면 파악하기 힘들다. 이를 해결할 수 있는 방법은 각 필드들에 대한 정보를 기입하고 그 필드들의 타입을 강제하여 발생할 수 있는 오류를 줄이는 것이다.
타입스크립트에서는 props에 대해서 타입을 지정한다.
가령 props로 전달하는 객체의 필드들이 name과 age가 있다고 할 때 아래와 같이 interface를 작성할 수 있다.
interface Props {
name: string;
age: number;
}
타입스크립트에서는 Array에 담기는 요소들의 타입을 지정할 수 있다.
가령 Todos라는 배열의 각 요소에 대한 정의가 필요하다고 할 때 interface를 작성할 수 있다.
interface TodoItem {
id: number;
content: string;
done: boolean;
}
interface Props {
todos: Array<TodoItem>;
}
타입스크립트에서는 메서드들의 타입들을 지정할 수 있다.
상태들과 그 상태들을 수정할 수 있는 메서드들을 동시에 props로 전달한다고 가정해보자. 그렇다면 메서드들에 대해서도 타입을 지정해주어야 한다.
interface Props {
todos: Array<TodoItem>;
addTodo: (content:string) => void;
removeTodo: (id: number) => void;
toggleTodo: (id: number) => void;
}
타입스크립트에서는 이벤트 핸들러 함수가 등록한 콜백함수가 받는 매개변수 event에 대해서 타입을 지정해주어야 한다.
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setText(e.target.value);
}
onChange 콜백함수를 전달한 React Element는 input이며 발생한 이벤트의 타입이 React.ChangeEvent<HTMLInputElement>임을 확인할 수 있다.
type FormEvent = React.FormEvent<HTMLFormElement>
type MouseEvent = React.MouseEvent<HTMLButtonElement>
type ChangeEvent = React.ChangeEvent<HTMLInputElement>
위 코드를 통해서 각 Element에 대해서 서로다른 event 객체의 타입을 확인할 수 있다.
타입스크립트에서는 useState Hook으로 관리하는 상태에 대한 타입을 지정할 수 있다.
const [count, setCounter] = useState<number>(0);
위 코드처럼 useState<type>에 타입을 지정할 수 있다.
타입스크립트에서는 Context의 타입을 지정할 수 있다.
interface TodoItem {
id: number;
content: string;
done: boolean;
};
interface TodoContextType {
todos: Array<TodoItem>;
addTodo: (content: string) => void;
removeTodo: (id: number) => void;
toggleTodo: (id: number) => void;
}
const TodoContext = React.createContext<TodoContextType>({
todos: [],
addTodo: () => {},
removeTodo: () => {},
toggleTodo: () => {},
});
Conext API에 대한 짧은 정리
Conext API는 Props Drilling을 방지하기 위해 사용할 수 있다. 먼저 Conext를 생성한 뒤 그 Conext를 통해서 Provider를 만들고 Provider 컴포넌트에 감싸진 컴포넌트들에 대해 상태를 Props로 전달하지 않고도 전달한다.
import React from 'react';
let nextId = 0;
interface TodoItem {
id: number;
content: string;
done: boolean;
};
interface TodoContextType {
todos: Array<TodoItem>;
addTodo: (content: string) => void;
removeTodo: (id: number) => void;
toggleTodo: (id: number) => void;
}
const TodoContext = React.createContext<TodoContextType>({
todos: [],
addTodo: () => {},
removeTodo: () => {},
toggleTodo: () => {},
});
interface Props {
children: React.ReactNode;
}
const TodoProvider = ({ children }: Props) => {
const [todos, setTodos] = React.useState<Array<TodoItem>>([]);
const addTodo = (content: string) => {
setTodos([...todos, {
id: nextId,
content,
done: false,
}])
nextId++;
};
const removeTodo = (id: number) => {
setTodos(todos.filter(todo => todo.id !== id));
}
const toggleTodo = (id: number) => {
setTodos(todos.map(todo => todo.id === id ? { ...todo, done: !todo.done } : todo));
}
return (
<TodoContext.Provider value={{ todos, addTodo, removeTodo, toggleTodo }}>
{children}
</TodoContext.Provider>
);
}
const useTodo = () => {
const context = React.useContext(TodoContext);
if (!context) {
throw new Error('Cannot find TodoProvider');
}
return context;
}
export { useTodo, TodoProvider };
위 코드는 Context를 정의한 코드이다. 주목할 부분은 Provider내부에 Children을 전달하여 감싸진 컴포넌트들에게 상태를 전달하는 부분이다
return (
<TodoContext.Provider value={{ todos, addTodo, removeTodo, toggleTodo }}>
{children}
</TodoContext.Provider>
);
또한 custom Hook을 만들어서 상태와 메서드들을 디스트럭처링 문법을 통해 편하게 전달 받을 수 있다.
const useTodo = () => {
const context = React.useContext(TodoContext);
if (!context) {
throw new Error('Cannot find TodoProvider');
}
return context;
}
'React' 카테고리의 다른 글
| React Native / Prettier설정 (0) | 2025.02.23 |
|---|---|
| React / React + Typescript / useMemo에 대하여... (0) | 2025.02.05 |
| React / React + Typescript / useEffect에 대하여... (3) | 2025.02.04 |
| React / React + Typescript / 초기 프로젝트 세팅 (0) | 2025.02.03 |
| 1. 리액트 라우터(React router) (0) | 2022.12.18 |