Open drizzle96 opened 2 months ago
import { useState } from 'react'
const TodoList = () => {
const [tasks, setTasks] = useState<string[]>([])
const [completedTasks, setCompletedTasks] = useState<string[]>([])
const [newTask, setNewTask] = useState('');
const addTask = (task: string) => {
setTasks((prev) => ([...prev, task]));
}
const completeTask = (index: number) => {
const task = tasks[index]
setTasks(prev => prev.filter((_, i) => i !== index))
setCompletedTasks(prev => [...prev, task])
}
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
addTask(newTask);
setNewTask('');
}
}
return (
<div>
<h1>Todo List</h1>
<ul>
{tasks.map((task, index) => (
<li key={index}>
{task}
<button onClick={() => completeTask(index)}>Complete</button>
</li>
))}
</ul>
<div>
<h2>Completed Tasks</h2>
<ul>
{completedTasks.map((task, index) => (
<li key={index}>{task}</li>
))}
</ul>
</div>
<input
type="text"
placeholder="New Task"
onChange={(e) => setNewTask(e.target.value)}
onKeyDown={handleKeyDown}
/>
</div>
)
}
export default TodoList
현재 TodoList는 tasks
와 completedTasks
를 상태 값으로 가지고 있습니다. 이 때 completeTask 함수를 보면, 어떤 task가 완료될 때 tasks
는 완료된 task를 filter하여 업데이트 되고, completedTasks
는 완료된 task를 배열에 추가하여 업데이트하고 있습니다.
즉 두 상태는 서로 연관된 계산으로 업데이트되는데 이는 위 가이드 중 2번 째를 위반한 것입니다. 각 상태는 서로 다른 출처에서 파생되어야 합니다. 각 상태의 출처가 서로 연관되어서는 안됩니다. 같은 출처에서 파생된 값들을 서로 다른 상태로 관리하는 식이 되면 데이터의 정확성과 일관성을 보장하기 어렵습니다.
코드에서 tasks
는 "완료되지 않은 일", completedTasks
는 "완료된 일"을 의미합니다. 둘을 같은 출처에서 관리하기 위해 "모든 일"을 의미하는 tasks
를 상태로 만들고, 두 값은 (새로 만든) tasks
상태로부터 값을 계산하여 변수로 담는 식으로 개선할 수 있습니다. 추가로 Task
타입을 만들어 tasks
상태 값의 타입을 Task[]
로 지정해줍니다.
${index}-${task.name}
와 같이 값을 만들어 넣어줍니다.import { useState } from 'react'
type Task = {
name: string
completed: boolean
}
const TodoList = () => {
const [tasks, setTasks] = useState<Task[]>([])
const [newTask, setNewTask] = useState('')
const incompletedTasks = tasks.filter((task) => !task.completed)
const completedTasks = tasks.filter((task) => task.completed)
const addTask = (name: string) => {
setTasks((prevTasks) => [...prevTasks, { name, completed: false }])
}
const completeTask = (index: number) => {
setTasks((prevTasks) => prevTasks.map((task, i) => (i === index ? { ...task, completed: true } : task)))
}
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
addTask(newTask)
setNewTask('')
}
}
return (
<div>
<h1>Todo List</h1>
<ul>
{incompletedTasks.map((task, index) => (
<li key={`${index}-${task.name}`}>
{task.name}
<button onClick={() => completeTask(index)}>Complete</button>
</li>
))}
</ul>
<div>
<h2>Completed Tasks</h2>
<ul>
{completedTasks.map((task, index) => (
<li key={`${index}-${task.name}`}>{task.name}</li>
))}
</ul>
</div>
<input
type="text"
placeholder="New Task"
value={newTask}
onChange={(e) => setNewTask(e.target.value)}
onKeyDown={handleKeyDown}
/>
</div>
)
}
export default TodoList
❓문제
다음 컴포넌트에서 상태 관리 가이드 법칙을 위반한 곳이 있다면 알려주세요. 그리고 타입스크립트를 적용하면서 이를 리팩토링 해주세요!
🎯답변