[React] 리액트 ToDoList 만들기 :: 컴포넌트 만들기
글 작성자: 망고좋아
반응형
🎯 React ToDoList - 컴포넌트 만들기
- 벨로퍼트님의 리액트를 사용하여 멋진 투두리스트를 보고 학습한 내용입니다.
📝 cra설정
npx create-react-app react-todolist
npm i react-icons styled-components
기존 폴더에서 cra
create-react-app .
📝 컴포넌트 역할
📕 TodoTemplate
- 투두리스트의 레이아웃을 설정하는 컴포넌트이다.
- 페이지 중앙에 그림자가 적용된 흰색 박스를 보여준다.
📕 TodoHead
- 오늘의 날짜와 요일을 보여주고, 앞으로 해야 할 일이 몇 개 남았는지 보여준다.
📕 TodoList
- 할 일에 대한 정보가 들어있는 todos 배열을 내장 함수
map
을 사용하여 여러 개의 TodoItem 컴포넌트를 렌더링 해준다.
📕 TodoItem
- 각 할 일에 대한 정보를 렌더링 해주는 컴포넌트이다.
- 좌측에 있는 원을 누르면 할 일의 완료 여부를 toggle 할 수 있다.
- 할 일이 완료됐을 땐 좌측에 체크가 나타나고 텍스트의 색상이 연해진다.
- 마우스를 올리면 휴지통 아이콘이 나타나고 이를 누르면 항목이 삭제된다.
📕 TodoCreate
- 새로운 할 일을 등록할 수 있게 해주는 컴포넌트이다.
- Todo Template의 하단부에 초록색 원 버튼을 렌더링 해주고, 이를 클릭하면 할 일을 입력할 수 있는 폼이 나타난다.
- 버튼을 다시 누르면 폼이 사라진다.
📝 페이지에 회색 배경 색상 적용 (feat. styled-components)
- styled-components에서 특정 컴포넌트를 만들어서 스타일링하는 게 아니라 글로벌 스타일을 추가하고 싶을 때는
createGlobalStyle
을 사용한다. 이 함수를 사용하면 컴포넌트가 만들어지는데, 이 컴포넌트를 렌더링 하면 된다.
🛠 App.js
import React from 'react';
import { createGlobalStyle } from 'styled-components';
const GlobalStyle = createGlobalStyle`
body {
background: #e9ecef;
}
`;
function App() {
return (
<>
<GlobalStyle />
<div>안녕하세요</div>
</>
);
}
export default App;
📝 TodoTemplate 만들기
- 중앙에 정렬된 흰색 박스 만들기
🛠 components/TodoTemplate.js
import React from "react";
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 rgba(0, 0, 0, 0.04);
margin: 0 auto;
margin-top: 96px;
margin-bottom: 32px;
display: flex;
flex-direction: column;
`;
function 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';
const GlobalStyle = createGlobalStyle`
body {
background: #e9ecef;
}
`;
function App() {
return (
<>
<GlobalStyle />
<TodoTemplate>안녕하세요</TodoTemplate>
</>
);
}
export default App;
- App 랜더링
📝 TodoHead 만들기
- 오늘의 날짜, 요일, 남은 할 일 개수를 보여주기
🛠 components/TodoHead.js
import React from "react";
import styled from "styled-components";
const TodoHeadBlock = styled.div`
padding: 48px 32px 24px 32px;
border-bottom: 1px solid #e9ecef;
h1 {
margin: 0px;
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;
}
`;
function TodoHead() {
return (
<TodoHeadBlock>
<h1>2021년 10월 5일</h1>
<div className="day">화요일</div>
<div className="tasks-left">할 일 2개 남음</div>
</TodoHeadBlock>
)
}
export default TodoHead;
- TodoHeadBlock 안에 들어 있는 내용들에 대해서 컴포넌트를 만드는 대신 일반 html태그를 사용하고 CSS Selector를 사용하여 스타일 적용
- 기능적으로 게 중요하지 않는 내용이면 CSS Selector를 사용하는 것도 좋은 방법이다.
🛠 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;
📝 TodoList 만들기
- 여러 개의 할 일 항목을 보여주게 될 TodoList 만들기
🛠 components/TodoList.js
import React from "react";
import styled from "styled-components";
const TodoListBlock = styled.div`
flex: 1;
padding: 20px 32px;
padding-bottom: 48px;
overflow-x: auto;
background: gray; /* 임시 스타일*/
`;
function TodoList() {
return <TodoListBlock>TodoList</TodoListBlock>
}
export default TodoList;
flex: 1;
을 설정해서 자신이 차지할 수 있는 영역을 꽉 채우도록 설정
📝 TodoItem 만들기
- 각 할 일 항목들을 보여주는 TodoItem 컴포넌트 만들기
- react-icons에서 MdDone과 MdDelete 아이콘을 사용
🛠 TodoItem.js
import React from 'react';
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;
}
}
`;
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;
`}
`;
function TodoItem({ id, done, text }) {
return (
<TodoItemBlock>
<CheckCircle done={done}>{done && <MdDone />}</CheckCircle>
<Text done={done}>{text}</Text>
<Remove>
<MdDelete />
</Remove>
</TodoItemBlock>
);
}
export default TodoItem;
TodoItemBlock
에서 Component Selector라는 기능을 사용TodoItemBlock
위에 커서가 있을 때,Remove
컴포넌트를 보여주라는 의미를 가지고 있다.
🛠 TodoList.js
import React from "react";
import styled from "styled-components";
import TodoItem from "./TodoItem";
const TodoListBlock = styled.div`
flex: 1;
padding: 20px 32px;
padding-bottom: 48px;
overflow-x: auto;
`;
function TodoList() {
return (
<TodoListBlock>
<TodoItem text="프로젝트 생성하기" done={true} />
<TodoItem text="컴포넌트 스타일링 하기" done={true} />
<TodoItem text="Context 만들기" done={false} />
<TodoItem text="기능 구현하기" done={false} />
</TodoListBlock>
);
}
export default TodoList;
- TodoItem 컴포넌트를 TodoList에서 랜더링 해주기
📝 TodoCreate 만들기
- 새로운 항목을 등록할 수 있는 컴포넌트
useState
를 사용하여 토글 할 수 있는 open 값을 관리하며, 이 값이true
일 때에는 아이콘을 45도 돌려서 X 모양이 보이게 한 후, 버튼 색상을 빨간색으로 바꿔준다.- 그리고 할 일을 입력할 수 있는 폼도 보여준다.
🛠 TodoCreate.js
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;
`;
function 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;
const [open, setOpen] = useState(false);
에서open
의 초기값으로 false를 설정해줬다.- 따라서
TodoCreate()
함수에서- open이 true면
InsertFormPositioner
- false면
CircleButton
을 보여준다.
- open이 true면
${props =>
props.open &&
css`
...
`}
- open이라는 props가 존재할 때(true) css로 정의된 스타일이 적용된다.
🛠 App.js
import React from 'react';
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;
🏷 요약
- styled-components에서
&:sth
의$
는 자기 선택자이다. - styled-components는 컴포넌트의 props를 전달받아 사용하는 것이 가능하다.
&&
연산자를 사용하면 해당 props가 존재하는 경우에는 정의한 css가 적용될 수 있도록 할 수 있다.
- 리액트에 다루는 데이터는 porps와 state로 나뉜다.
- porps란 properties로 컴포넌트 속성을 설정할 때 사용한다.
- 자식 컴포넌트에서는 props를 받아오기만 하고, 받아온 props를 직접 수정할 수는 없다. 자식 컴포넌트에서는 props는 읽기 전용 데이터이다.
- state는 컴포넌트 내부에서 선언하며 내부에서 값을 변경할 수 있다.
📌 참고
반응형
'프로그래밍 > React' 카테고리의 다른 글
[React] 리액트 JSX (0) | 2021.12.30 |
---|---|
[React / Error] 리액트 Module not found: Error: Can't resolve 'react' in 에러 해결 방법 (0) | 2021.10.23 |
[React] Ref로 DOM 다루기 :: useRef (0) | 2021.09.14 |
[React] Hook Flow 이해하기 2 :: App - Child, Clean-up (0) | 2021.09.14 |
[React] Hook Flow 이해하기 1 :: App - Child (0) | 2021.09.14 |
댓글
이 글 공유하기
다른 글
-
[React] 리액트 JSX
[React] 리액트 JSX
2021.12.30 -
[React / Error] 리액트 Module not found: Error: Can't resolve 'react' in 에러 해결 방법
[React / Error] 리액트 Module not found: Error: Can't resolve 'react' in 에러 해결 방법
2021.10.23 -
[React] Ref로 DOM 다루기 :: useRef
[React] Ref로 DOM 다루기 :: useRef
2021.09.14 -
[React] Hook Flow 이해하기 2 :: App - Child, Clean-up
[React] Hook Flow 이해하기 2 :: App - Child, Clean-up
2021.09.14