App.jsx
더보기
import { useState } from 'react';
import './App.css';
function App() {
const [todoList, setTodoList] = useState([
{ id: 0, content: '밥먹기' },
{ id: 1, content: '공부하기' },
{ id: 2, content: '잠자기' },
]);
return (
<>
<TodoInput todoList={todoList} setTodoList={setTodoList} />
<hr />
<TodoList todoList={todoList} setTodoList={setTodoList} />
</>
);
}
function TodoInput({ todoList, setTodoList }) {
const [inputValue, setInputValue] = useState('');
const insertTodo = () => {
if (!inputValue.trim()) {
alert('글자를 입력해주세요.');
return;
}
const newTodo = { id: Number(new Date()), content: inputValue };
const newTodoList = [...todoList, newTodo];
setTodoList(newTodoList);
setInputValue('');
};
return (
<>
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button onClick={insertTodo}>추가하기</button>
</>
);
}
function TodoList({ todoList, setTodoList }) {
return (
<ul>
{todoList.map((todo) => (
<Todo key={todo.id} todo={todo} setTodoList={setTodoList} />
))}
</ul>
);
}
function Todo({ todo, setTodoList }) {
const [inputValue, setInputValue] = useState('');
const updateTodo = () => {
// 빈 문자열일 경우
if (!inputValue.trim()) {
alert('글자를 입력해주세요.');
return;
}
setTodoList((prev) =>
prev.map((el) =>
el.id === todo.id ? { ...el, content: inputValue } : el,
),
);
setInputValue('');
};
const deleteTodo = () => {
setTodoList((prev) => {
return prev.filter((el) => el.id !== todo.id);
});
};
return (
<li>
{todo.content}{' '}
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button onClick={updateTodo}>수정</button>
<button onClick={deleteTodo}>삭제</button>
</li>
);
}
export default App;
App.jsx에 컴포넌트들을 다 넣어줬고 각 컴포넌트는 다음과 같다.
- App: todoList의 상태를 갖고 있는 최상위 컴포넌트
- TodoInput: 새로운 할 일을 입력하고 추가하는 컴포넌트
- TodoList: 할 일 목록을 출력하는 컴포넌트
- Todo: 각 할 일 항목 하나를 나타내는 컴포넌트 (수정, 삭제 기능 포함)
App
function App() {
const [todoList, setTodoList] = useState([
{ id: 0, content: '밥먹기' },
{ id: 1, content: '공부하기' },
{ id: 2, content: '잠자기' },
]);
// 자식 컴포넌트 TodoInput, TodoList에게 todoList들의 상태를 담은 props를 전달
return (
<>
<TodoInput todoList={todoList} setTodoList={setTodoList} />
<hr />
<TodoList todoList={todoList} setTodoList={setTodoList} />
</>
);
}
TodoInput
function TodoInput({ todoList, setTodoList }) {
// input 요소의 상태 생성
const [inputValue, setInputValue] = useState('');
const insertTodo = () => {
// 빈 문자열 방지
if (!inputValue.trim()) {
alert('글자를 입력해주세요.');
return;
}
// id의 고유성을 위해 Date()객체를 생성해 부여한다
const newTodo = { id: Number(new Date()), content: inputValue };
// 새 변수 newTodoList : 기존 리스트를 스프레드 연산자로 복사, 새 할일이 담겨있음
const newTodoList = [...todoList, newTodo];
// newTodoList가 setTodoList로 업데이트됨
setTodoList(newTodoList);
setInputValue('');
}
return (
<>
{/* 초기 value는 inputValue,
value가 변경될 시 setInputValue 함수로 value가 바뀌게 설정 */}
<input value={inputValue} onChange={(e) => setInputValue(e.target.value)} />
<button
{/* 클릭 시 이벤트 */}
onClick={insertTodo}>
추가하기
</button>
</>
);
}
todoList
function TodoList({ todoList, setTodoList }) {
return (
<ul>
{/* todoList를 map으로 순회해 Todo 컴포넌트 렌더링 */}
{todoList.map((todo) => (
<Todo key={todo.id} todo={todo} setTodoList={setTodoList} />
))}
</ul>
);
}
todo
리액트는 불변성을 중요시하기 때문에 스프레드 연산자로 기존 배열을 직접 바꾸지 않고 복사한 새 배열을 생성해 교체하면,
변경 사항을 감지하고 화면이 리렌더링된다.
function Todo({ todo, setTodoList }) {
// input의 상태 생성
const [inputValue, setInputValue] = useState('');
// 수정 기능
const updateTodo = () => {
// 빈 문자열일 경우
if (!inputValue.trim()) {
alert('글자를 입력해주세요.');
return;
}
// 빈 문자열이 아니면
setTodoList((prev) =>
// 기존 todoList 배열을 하나씩 순회하면서 각 el(element)확인
prev.map((el) =>
// el.id === todo.id인 항목의 content만 수정, 아니면 그냥 el 반환
el.id === todo.id ? { ...el, content: inputValue } : el,
),
); // 새로운 배열이 상태로 들어감 => 리렌더링됨
// input창 초기화
setInputValue('');
};
// 삭제 기능
const deleteTodo = () => {
setTodoList((prev) => {
// 클릭한 항목의 id와 일치하지 않는 것만 남김 (삭제 버튼을 클릭한 항목은 제거됨)
return prev.filter((el) => el.id !== todo.id);
});
};
return (
<li>
{todo.content}{' '}
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button onClick={updateTodo}>수정</button>
<button onClick={deleteTodo}>삭제</button>
</li>
);
}
'react' 카테고리의 다른 글
React / Router (0) | 2025.04.09 |
---|---|
React / 조건부 렌더링 (0) | 2025.04.09 |
React / props, state 끌어올리기 (0) | 2025.04.06 |
React / SPA vs MPA (0) | 2025.04.04 |
React / 상태관리 (0) | 2025.04.04 |