
어제에 이어서 추가 진행된 부분
1) 삭제 기능
2) 완료 기능
3) 취소 기능
4) 상세페이지 토글창 구현
삭제까지는 유데미 영상을 보면서 클론코딩으로 진행했고
이후 기능은
깃허브링크
GitHub - jeLee94/type_prac
Contribute to jeLee94/type_prac development by creating an account on GitHub.
github.com
0. 이미지

1. 삭제 기능
TodoItems.tsx는 Todos에서 RemoveItem을 받아온다.
Todos.tsx는 App.tsx에서 onDeleteHandler를 받아온다.
App.tsx에서는 state를 set하도록 함수를 정의한다.



2. 완료/취소 기능
흐름은 위와 같고 App.tsx에서 정의하는 함수만 다르다
onDoneHandler에 이벤트가 발생하면 매개변수로 전달받은 todoItem의 isDone이 토글된다.
true일 때는 완료가 된 상태이므로
todos에는 현재 완료 버튼을 누른 todo를 제외한 나머지만 남게하고
doneTodos에는 이전에 doneTodos의 데이터와 현재 todo를 합친다.
false일 때는 done -> todo로 취소를 하는 상황이므로
true일 때와 반대로 작성한다.

3. 상세 페이지 구현
todo 컴포넌트 하나를 누르면 우측에 화면이 토글되도록 한다.
todo 하나를 눌러 활성화 된 상태에서 다른 todo를 누르면 토글되지 않고 다른 todo의 내용으로 변환되도록 한다.

4. 코드
App.tsx
function App() { const [allList, setAllList] = useState<Todo[]>([]); //상세 토글 창을 위해 전체 리스트 생성 const [todos, setTodos] = useState<Todo[]>([]); const [doneTodos, setDoneTodos] = useState<Todo[]>([]); const [isDetailShown, setIsDetailShown] = useState(false); const [onClickId, setOnClickId] = useState(''); const addTodoHandler = (todoText: string) => { const newTodo = new Todo(todoText); setTodos((prevTodos) => { return prevTodos.concat(newTodo); }); setAllList((prevTodos) => { return prevTodos.concat(newTodo); }); }; const onDeleteHandler = (todoId: string) => { if (window.confirm('정말 삭제하시겠습니까?')) { setTodos((prevTodos) => { return prevTodos.filter((todo) => todo.id !== todoId); }); setDoneTodos((prevTodos) => { return prevTodos.filter((todo) => todo.id !== todoId); }); setAllList((prevTodos) => { return prevTodos.filter((todo) => todo.id !== todoId); }); } }; const onDoneHandler = (todoItem: Todo) => { todoItem.isDone = !todoItem.isDone; if (todoItem.isDone) { setTodos((prevTodos) => { return prevTodos.filter((todo) => todo.id !== todoItem.id); }); setDoneTodos((prevTodos) => { return prevTodos.concat( todos.filter((todo) => todo.id === todoItem.id) ); }); } else { setTodos((prevTodos) => { return prevTodos.concat(todoItem); }); setDoneTodos((prevTodos) => { return prevTodos.filter((todo) => todo.id !== todoItem.id); }); } }; const onDetailHandler = (todoItem: Todo) => { console.log('클릭!'); !isDetailShown ? setIsDetailShown(true) : setOnClickId(todoItem.id); //디테일 창이 false이면 보여주고 보여주고 있으면 내용 변경 todoItem.id === onClickId && isDetailShown === true ? setIsDetailShown(false) : setOnClickId(todoItem.id); setOnClickId(todoItem.id); }; return ( <Div> <MainDiv> <AddTodo onAddTodo={addTodoHandler} /> <Todos todoItems={todos} doneItems={doneTodos} onDeleteHandler={onDeleteHandler} onDoneHandler={onDoneHandler} onDetailHandler={onDetailHandler} /> </MainDiv> {/* 상세페이지 토글기능 */} {isDetailShown ? allList.map((todo) => todo.id === onClickId ? <DetailPage todo={todo} /> : null ) : null} </Div> ); }
Todos.tsx
import React from 'react'; import styled from 'styled-components'; import Todo from '../models/todo'; import TodoItem from './TodoItems'; // import DoneItem from './DoneItem'; const Todos: React.FC<{ todoItems: Todo[]; doneItems: Todo[]; onDeleteHandler: (todoId: string) => void; onDoneHandler: (todo: Todo) => void; onDetailHandler: (todo: Todo) => void; }> = (props) => { return ( <Div> <TodoDiv> <p>To Do</p> {props.todoItems.map((item) => ( <TodoItem key={item.id} item={item} onRemoveItem={props.onDeleteHandler.bind(null, item.id)} onDoneItem={props.onDoneHandler.bind(null, item)} onDetailItem={props.onDetailHandler.bind(null, item)} /> ))} </TodoDiv> <hr /> <DoneDiv> <p>Done</p> {props.doneItems.map((item) => ( <TodoItem key={item.id} item={item} onRemoveItem={props.onDeleteHandler.bind(null, item.id)} onDoneItem={props.onDoneHandler.bind(null, item)} onDetailItem={props.onDetailHandler.bind(null, item)} /> ))} </DoneDiv> </Div> ); }; export default Todos; const Div = styled.div` display: flex; `; const TodoDiv = styled.div` width: 50%; display: flex; flex-direction: column; align-items: center; height: 74vh; overflow: auto; `; const DoneDiv = styled.div` width: 50%; display: flex; flex-direction: column; align-items: center; height: 74vh; overflow: auto; `;
TodoItems.tsx
import React from 'react'; import styled from 'styled-components'; import Todo from '../models/todo'; const TodoItem: React.FC<{ item: Todo; onRemoveItem: (event: React.MouseEvent) => void; onDoneItem: (event: React.MouseEvent) => void; onDetailItem: (event: React.MouseEvent) => void; }> = (props) => { return ( <Div> <TextDiv onClick={props.onDetailItem}>{props.item.text}</TextDiv> <p> <button onClick={props.onDoneItem}> {props.item.isDone ? '취소' : '완료'} </button> <button onClick={props.onRemoveItem}>삭제</button> </p> </Div> ); }; export default TodoItem; const Div = styled.div` border: 1px solid black; border-radius: 10px; display: flex; flex-direction: column; align-items: center; justify-content: center; width: 50%; height: 7.5rem; margin: 20px; `; const TextDiv = styled.div` width: 100%; height: 4rem; display: flex; align-items: center; justify-content: center; `;
DetailPage.tsx
import styled from 'styled-components'; import Todo from '../models/todo'; const DetailPage: React.FC<{ todo: Todo }> = (props) => { return ( <DetailDiv> <div>{props.todo.id}</div> <div>{props.todo.text}</div> <div> <button>수정</button> <button>삭제</button> </div> </DetailDiv> ); }; export default DetailPage; const DetailDiv = styled.div` width: 60vw; height: 80vh; margin: 5rem auto; background-color: gray; display: flex; flex-direction: column; justify-content: center; align-items: center; `;
'매일 해내는 개발 > Develog' 카테고리의 다른 글
[develog] React 아웃소싱 프로젝트 S.A (2) | 2023.01.25 |
---|---|
유튜브 API 사용하는 방법 링크 모음 (0) | 2023.01.21 |
[Develog] 타입스크립트로 TodoList만들어보기 (0) | 2023.01.19 |
[Develog] 리액트 + 타입스크립트 프로젝트 만들기 (0) | 2023.01.18 |
[Develog] 타입스크립트 기초 (0) | 2023.01.17 |
댓글