-
[React] Todolist 만들기 정리(1)📌 React 2022. 9. 28. 00:04
벨로퍼트님의 https://react.vlpt.us/mashup-todolist/01-create-components.html 보고 직접 구현했던 과정들을 정리하려고 한다.
cra설정
yarn create react-app react-todolist
이번 프로젝트에서 필요한 라이브러리 react-icons 와 styled-components 를 설치.
$ cd react-todolist $ yarn add react-icons styled-components
컴포넌트 정리
TodoProvider
- useReducer 를 사용하여 상태를 관리하는 컴포넌트이다.
TodoTemplate
- 투두리스트의 레이아웃을 설정하는 컴포넌트이다.
- 페이지 중앙에 그림자가 적용된 흰색 박스를 보여준다.
TodoHead
- 오늘의 날짜와 요일을 보여주고, 앞으로 해야 할 일이 몇 개 남았는지 보여준다.
TodoList
- 할 일에 대한 정보가 들어있는 todos 배열을 내장 함수 map을 사용하여 여러 개의 TodoItem 컴포넌트를 렌더링 해준다.
TodoItem
- 각 할 일에 대한 정보를 렌더링 해주는 컴포넌트이다.
- 좌측에 있는 원을 누르면 할 일의 완료 여부를 toggle 할 수 있다.
- 할 일이 완료됐을 땐 좌측에 체크가 나타나고 텍스트의 색상이 연해진다.
- 마우스를 올리면 휴지통 아이콘이 나타나고 이를 누르면 항목이 삭제된다.
TodoCreate
- 새로운 할 일을 등록할 수 있게 해주는 컴포넌트이다.
- Todo Template의 하단부에 초록색 원 버튼을 렌더링 해주고, 이를 클릭하면 할 일을 입력할 수 있는 폼이 나타난다.
- 버튼을 다시 누르면 폼이 사라진다.
App.js
import React from "react"; import { createGlobalStyle } from "styled-components"; import TodoTemplate from "./components/TodoTemplate"; const GlobalStyle = createGlobalStyle` body { background: #e9ecef; } `; function App() { return ( <> <GlobalStyle /> <TodoTemplate>안녕하세요</TodoTemplate> </> ); } export default App;
src / components / TodoTemplate.js
import styled from "styled-components"; const TodoTemplateBlock = styled.div` width: 512px; height: 768px; position: relative; background: white; border-radius: 16px; box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.04); margin: 0 auto; margin-top: 96px; margin-bottom: 32px; display: flex; flex-direction: column; `; const TodoTemplate = ({ children }) => { return <TodoTemplateBlock>{children}</TodoTemplateBlock>; }; export default TodoTemplate;
위 두개의 결과 화면
App.js
import React from "react"; import { createGlobalStyle } from "styled-components"; import TodoTemplate from "./components/TodoTemplate"; import TodoHead from "./components/TodoHead"; const GlobalStyle = createGlobalStyle` body { background: #e9ecef; } `; function App() { return ( <> <GlobalStyle /> <TodoTemplate> <TodoHead /> </TodoTemplate> </> ); } export default App;
src/components/TodoHead.js
import React from "react"; import styled from "styled-components"; const TodoHeadBlock = styled.div` padding-top: 48px; padding-left: 32px; padding-right: 32px; padding-bottom: 24px; border-bottom: 1px solid #e9ecef; h1 { margin: 0; font-size: 36px; color: #343a40; } .day { margin-top: 4px; color: #868e96; font-size: 21px; } .tasks-left { color: #20c997; font-size: 18px; margin-top: 40px; font-weight: bold; } `; const TodoHead = () => { return ( <TodoHeadBlock> <h1>2022년 10월 01일</h1> <div className="day">토요일</div> <div className="tasks-left">할 일 3개 남음</div> </TodoHeadBlock> ); }; export default TodoHead;
App.js
import { createGlobalStyle } from "styled-components"; import TodoTemplate from "./components/TodoTemplate"; import TodoHead from "./components/TodoHead"; import TodoList from "./components/TodoList"; const GlobalStyle = createGlobalStyle` body { background: #e9ecef; } `; function App() { return ( <> <GlobalStyle /> <TodoTemplate> <TodoHead /> <TodoList /> </TodoTemplate> </> ); } export default App;
src/components/TodoList.js
import styled from "styled-components"; const TodoListBlock = styled.div` flex: 1; padding: 20px 32px; padding-bottom: 48px; overflow-y: auto; background: gray; /* 사이즈 조정이 잘 되고 있는지 확인하기 위한 임시 스타일 */ `; const TodoList = () => { return <TodoListBlock>TodoList</TodoListBlock>; }; export default TodoList;
src/components/TodoItem.js
이 컴포넌트에서는 react-icons 에서 MdDone 과 MdDelete 아이콘을 사용.
import styled, { css } from "styled-components"; import { MdDone, MdDelete } from "react-icons/md"; const Remove = styled.div` display: flex; align-items: center; justify-content: center; color: #dee2e6; font-size: 24px; cursor: pointer; &:hover { color: #ff6b6b; } display: none; `; const TodoItemBlock = styled.div` display: flex; align-items: center; padding-top: 12px; padding-bottom: 12px; &:hover { ${Remove} { display: initial; } } /* 여기서 사용된 기능은 Component Selector 라는 기능입니다. 이 스타일은 TodoItemBlock 위에 커서가 있을 때, Remove 컴포넌트를 보여주라는 의미를 가지고 있습니다. */ `; const CheckCircle = styled.div` width: 32px; height: 32px; border-radius: 16px; border: 1px solid #ced4da; font-size: 24px; display: flex; align-items: center; justify-content: center; margin-right: 20px; cursor: pointer; ${(props) => props.done && css` border: 1px solid #38d9a9; color: #38d9a9; `} `; const Text = styled.div` flex: 1; font-size: 21px; color: #495057; ${(props) => props.done && css` color: #ced4da; `} `; const TodoItem = ({ id, done, text }) => { return ( <TodoItemBlock> <CheckCircle done={done}>{done && <MdDone />}</CheckCircle> <Text done={done}>{text}</Text> <Remove> <MdDelete /> </Remove> </TodoItemBlock> ); }; export default TodoItem;
src/components/TodoList.js
import styled from "styled-components"; import TodoItem from "./TodoItem"; const TodoListBlock = styled.div` flex: 1; padding: 20px 32px; padding-bottom: 48px; overflow-y: auto; `; const TodoList = () => { return ( <TodoListBlock> <TodoItem text="프로젝트 생성하기" done={true} /> <TodoItem text="컴포넌트 스타일링 하기" done={true} /> <TodoItem text="Context 만들기" done={false} /> <TodoItem text="기능 구현하기" done={false} /> </TodoListBlock> ); }; export default TodoList;
src/components/TodoCreate.js
이 컴포넌트에서는 useState 를 사용하여 토글 할 수 있는 open 값을 관리한다.
이 값이 true 일 때에는 아이콘을 45도 돌려서 X 모양이 보여지게 한 후, 버튼 색상을 빨간색으로 바꿔준다.
그리고, 할 일을 입력 할 수 있는 폼도 보여준다
import React, { useState } from "react"; import styled, { css } from "styled-components"; import { MdAdd } from "react-icons/md"; const CircleButton = styled.button` background: #38d9a9; &:hover { background: #63e6be; } &:active { background: #20c997; } z-index: 5; cursor: pointer; width: 80px; height: 80px; display: block; align-items: center; justify-content: center; font-size: 60px; position: absolute; left: 50%; bottom: 0px; transform: translate(-50%, 50%); color: white; border-radius: 50%; border: none; outline: none; display: flex; align-items: center; justify-content: center; transition: 0.125s all ease-in; ${(props) => props.open && css` background: #ff6b6b; &:hover { background: #ff8787; } &:active { background: #fa5252; } transform: translate(-50%, 50%) rotate(45deg); `} `; const InsertFormPositioner = styled.div` width: 100%; bottom: 0; left: 0; position: absolute; `; const InsertForm = styled.form` background: #f8f9fa; padding-left: 32px; padding-top: 32px; padding-right: 32px; padding-bottom: 72px; border-bottom-left-radius: 16px; border-bottom-right-radius: 16px; border-top: 1px solid #e9ecef; `; const Input = styled.input` padding: 12px; border-radius: 4px; border: 1px solid #dee2e6; width: 100%; outline: none; font-size: 18px; box-sizing: border-box; `; const TodoCreate = () => { const [open, setOpen] = useState(false); const onToggle = () => setOpen(!open); return ( <> {open && ( <InsertFormPositioner> <InsertForm> <Input autoFocus placeholder="할 일을 입력 후, Enter 를 누르세요" /> </InsertForm> </InsertFormPositioner> )} <CircleButton onClick={onToggle} open={open}> <MdAdd /> </CircleButton> </> ); }; export default TodoCreate;
App.js
import { createGlobalStyle } from "styled-components"; import TodoTemplate from "./components/TodoTemplate"; import TodoHead from "./components/TodoHead"; import TodoList from "./components/TodoList"; import TodoCreate from "./components/TodoCreate"; const GlobalStyle = createGlobalStyle` body { background: #e9ecef; } `; function App() { return ( <> <GlobalStyle /> <TodoTemplate> <TodoHead /> <TodoList /> <TodoCreate /> </TodoTemplate> </> ); } export default App;
UI 구현은 여기까지!
반응형'📌 React' 카테고리의 다른 글
[React] Todolist 만들기 정리(3) (0) 2022.09.29 [React] Todolist 만들기 정리(2) (0) 2022.09.28 [React] Context API (0) 2022.09.27 [React] 데이터를 저장하는 방법 (0) 2022.09.27 [React] 데이터 내용을 선택적으로 table 안에 보여주기 (0) 2022.09.27