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>
와 같이 작성해도 된다.