
module.exports = {
root: true,
env: {
browser: true,
node: true,
},
extends: [
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
],
rules: {
"prettier/prettier": [
"error",
{
doubleQuote: true,
semi: true,
useTabs: false,
tabWidth: 4,
printWidth: 80,
bracketSpacing: true,
arrowParens: "avoid",
},
],
},
parserOptions: {
parser: "@typescript-eslint/parser",
},
};
{
"compilerOptions": {
"jsx": "react-jsx",
"lib": ["es6", "dom"],
"rootDir": "src",
"module": "CommonJS",
"esModuleInterop": true,
"target": "es5",
"sourceMap": true,
"moduleResolution": "node",
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": true,
"allowJs": true
},
"include": ["./src"],
"exclude": ["node_modules", "build"]
}
export 시켜서,// 각 개별 투두 데이터의 타입 정의
type TodoType = {
id: number;
text: string;
isComplete?: boolean;
};
// 각 투두 항목 컴포넌트가 받는 props의 타입 정의
type TodoProps = {
todos: TodoType[];
completeTodo: (id: number) => void;
removeTodo: (id: number) => void;
};
// 투두를 작성하는 TodoForm 컴포넌트가 받는 props의 타입 정의
type TodoFormProps = {
onSubmit: (todo: TodoType) => void;
};
export { TodoType, TodoProps, TodoFormProps };
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
as HTMLElement를 달아주는 것! import TodoForm from "./component/TodoForm";
import Todo from "./component/Todo";
import { useState } from "react";
import "./todos.css";
import { TodoType } from "./types/types";
function Todos() {
const [todos, setTodos] = useState<TodoType[]>([]);
const addTodo = (todo: TodoType) => {
if (!todo.text || /^\s*$/.test(todo.text)) {
return;
}
const newTodos: TodoType[] = [todo, ...todos];
setTodos(newTodos);
};
const removeTodo = (id: number) => {
const removeArr = [...todos].filter((todo: TodoType) => todo.id !== id);
setTodos(removeArr);
};
const completeTodo = (id: number) => {
const completedTodo = todos.map((todo: TodoType) => {
if (todo.id === id) {
todo.isComplete = !todo.isComplete;
}
return todo;
});
setTodos(completedTodo);
};
return (
<div>
<div className="todo-app">
<h1>To Do List</h1>
<h2>오늘은 무슨 일을 계획하나요?</h2>
<TodoForm onSubmit={addTodo} />
<Todo
todos={todos}
completeTodo={completeTodo}
removeTodo={removeTodo}
/>
</div>
</div>
);
}
export default Todos;
App.js의 구조는 다음과 같다.
<h1> & 작은 제목 <h2> onSubmit={addTodo} 함수를 넘겨주고 있다. todos, 투두를 클리어하는 함수 completeTodo, 투두를 삭제하는 removeTodo 함수를 전달하고 있다. props로 넘겨주는 함수와 데이터들은 모두 App.js에서 정의하고 있다.
전체 투두 목록을 담고있는 변수 todos의 타입은 types.tsx에서 import한 TodoType 을 적용.
addTodo 함수의 매개변수는 변수 todos에 새로이 추가되는 todo 항목으로, TodoType의 타입을 가진다.
todos에서 특정 todo 항목을 삭제하는 함수인 deleteTodo 함수의 매개변수는 지우고자하는 특정 todo의 id로 number 타입이다.
특정 todo의 isComplete 값에 변경을 주는 completeTodo 함수의 매개변수 역시 변화를 주고자 하는 특정 todo의 id이므로, number 타입을 준다.
import { TodoProps } from "../types/types";
function Todo({ todos, completeTodo, removeTodo }: TodoProps) {
return (
<div className="wrapper-todo">
{todos.map((todo, index) => {
const todoClass = todo.isComplete
? "todo-row complete"
: "todo-row";
return (
<div className={todoClass} key={index}>
<div
key={todo.id}
onClick={() => completeTodo(todo.id)}
>
{todo.text}
</div>
<div className="icons">
<i
className="fas fa-times delete-icon"
onClick={() => removeTodo(todo.id)}
></i>
</div>
</div>
);
})}
</div>
);
}
export default Todo;
- 개별 todo를 보여주는 컴포넌트
{todos, completeTodo, deleteTodo}는 types.tsx에서 정의한 Todoprops 타입을 import해서 적용 // 각 투두 항목 컴포넌트가 받는 props의 타입 정의
type TodoProps = {
todos: TodoType[];
completeTodo: (id: number) => void;
removeTodo: (id: number) => void;
};import { useState, useEffect, useRef } from "react";
import { TodoFormProps } from "../types/types";
function TodoForm(props: TodoFormProps) {
const [input, setInput] = useState<string>("");
const [number, setNumber] = useState<number>(1);
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
}
});
const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
setInput(e.target.value);
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>): void => {
e.preventDefault();
setNumber(number + 1);
props.onSubmit({
id: number,
text: input,
});
setInput("");
};
return (
<form id="todoForm" className="todo-form" onSubmit={handleSubmit}>
<input
type="text"
placeholder="Add a todo"
value={input}
name="text"
className="todo-input"
onChange={handleChange}
ref={inputRef}
/>
<button className="todo-button">Add todo</button>
</form>
);
}
export default TodoForm;
- 새로 추가할 todo를 입력하는 form 컴포넌트
매개변수 props의 타입은 types.tsx 파일에 정의한 TodoFormProps를 import 해서 적용
type TodoFormProps = {
onSubmit: (todo: TodoType) => void;
};
TodoForm 컴포넌트는 App.tsx에서 onSubmit={addTodo}를 전달인자로 받고 있다.useState에 모두 타입을 지정해준다. input은 string, number는 number 타입, useRef의 경우 <HTMLInputElement> 을 타입으로 지정해주면 된다.
handleChange와 handleSubmit과 같은 이벤트 핸들러 함수의 경우 넘겨받는 매개변수의 타입을 e: React.ChangeEvent<HTMLInputElement>로 설정해주면 된다.
React.ChangeEvent<HTMLInputElement> 대신, 최상단에import { ChangeEvent, FormEvent } from "react"; 를 작성해주면e: ChangeEvent<HTMLInputElement> 와 같이 작성해도 된다.