React To-Do List
2024. 12. 19. 14:25ㆍ웹 개발
App.jsx
import React, { useState, useRef, useCallback } from 'react';
import TodoTemplate from './components/TodoTemplate';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
const App = () => {
const [todos, setTodos] = useState([
{
id: 1,
text: '기초 알아보기',
checked: true,
},
{
id: 2,
text: '컴포넌트 스타일링해 보기',
checked: true,
},
{
id: 3,
text: '일정 관리 앱 만들어 보기',
checked: false,
},
]);
const nextId = useRef(todos.length + 1); // 다음 추가될 할 일의 id (4부터 시작)
const onInsert = useCallback(
// 할 일 추가
(text) => {
const todo = {
id: nextId.current,
text,
checked: false,
};
setTodos(todos.concat(todo));
nextId.current += 1; // nextId 1 씩 더하기
},
[todos]
);
const onRemove = useCallback(
// 할 일 삭제
(id) => {
setTodos(todos.filter((todo) => todo.id !== id)); // id가 일치하지 않는 todo만 남긴 배열로 상태 갱신
},
[todos] // todos 상태가 변경될 때마다 함수가 재실행되도록 설정
);
const onToggle = useCallback(
// 상태 토글
(id) => {
setTodos(
todos.map(
(todo) =>
todo.id === id
? { ...todo, checked: !todo.checked } // 해당 id의 todo를 찾아 체크 상태를 반전
: todo // 그 외의 todo는 그대로 반환
)
);
},
[todos]
);
return (
<TodoTemplate>
<TodoInsert onInsert={onInsert} />
<TodoList todos={todos} onRemove={onRemove} onToggle={onToggle} />
</TodoTemplate>
);
};
export default App;
TodoTemplate.jsx
import React from 'react';
import '../styles/TodoTemplate.scss';
const TodoTemplate = ({ children }) => {
return (
<div className="TodoTemplate">
<div className="app-title">일정 관리</div>
<div className="content">{children}</div>
</div>
);
};
export default TodoTemplate;
TodoInsert.jsx
import React, { useState, useCallback } from 'react';
import { MdAdd } from 'react-icons/md'; // 아이콘 라이브러리에서 MdAdd 아이콘 가져오기
import '../styles/TodoInsert.scss'; // 스타일 시트 임포트
const TodoInsert = ({ onInsert }) => {
// 입력값을 관리하는 state
const [value, setValue] = useState('');
const onChange = useCallback((e) => {
setValue(e.target.value); // input의 값이 변경되면 value state 업데이트
}, []); // 컴포넌트가 처음 렌더링될 때만 실행됨 (의존성 배열이 비어있으므로)
const onSubmit = useCallback(
(e) => {
e.preventDefault();
onInsert(value);
setValue(''); // 입력값 초기화
},
[onInsert, value]
); // onInsert와 value가 변경될 때마다 실행됨
return (
<form className="TodoInsert" onSubmit={onSubmit}>
<input
value={value} // input 값은 state value와 연결
onChange={onChange}
placeholder="할 일을 입력하세요"
/>
<button type="submit">
<MdAdd />
</button>
</form>
);
};
export default TodoInsert;
TodoList.jsx
import React from 'react';
import TodoListItem from './TodoListItem';
import '../styles/TodoList.scss';
const TodoList = ({ todos, onRemove, onToggle }) => {
return (
<div className="TodoList">
{todos.map((todo) => (
<TodoListItem
todo={todo}
key={todo.id}
onRemove={onRemove}
onToggle={onToggle}
/>
))}
</div>
);
};
export default TodoList;
TodoListItem.jsx
import React from 'react';
import classnames from 'classnames';
import '../styles/TodoListItem.scss';
import {
MdCheckBoxOutlineBlank,
MdCheckBox,
MdRemoveCircleOutline,
} from 'react-icons/md';
const TodoListItem = ({ todo, onRemove, onToggle }) => {
// todo 객체에서 id, text, checked 값 추출
const { id, text, checked } = todo;
// style
const linkStyle = {
userSelect: 'none',
};
return (
<div className="TodoListItem">
<span
style={linkStyle}
className={classnames('checkbox', { checked })} // checked가 true이면 'checked' 클래스를 추가
onClick={() => onToggle(id)} // 클릭 시 onToggle 함수 호출하여 체크 상태 변경
>
{checked ? <MdCheckBox /> : <MdCheckBoxOutlineBlank />}
<span className="text">{text}</span>
</span>
<div className="remove" onClick={() => onRemove(id)}>
<MdRemoveCircleOutline />
</div>
</div>
);
};
export default TodoListItem;
https://stackblitz.com/edit/vitejs-vite-251wr6js?embed=1&file=src%2FApp.jsx
'웹 개발' 카테고리의 다른 글
React Router DOM (1) | 2024.12.20 |
---|---|
React Immer (0) | 2024.12.19 |
React CSS 스타일링 방법 정리 (0) | 2024.12.19 |
React-icons (0) | 2024.12.18 |
React Playground / playcode.io (0) | 2024.12.18 |