728x90
반응형
TodoHead
남은 할일 > done이 false 인것의 갯수를 출력
import React from "react";
import styled from "styled-components";
import {useTodoState} from "./TodoContext";
const TodoHeadBlock = styled.div`
padding-top: 48px;
padding-left: 32px;
padding-right: 24px;
padding-left: 32px;
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;
}
`;
function TodoHead() {
const todos = useTodoState();
const undoneTasks = todos.filter(todo => !todo.done);
return (
<TodoHeadBlock>
<h1>2022년 08월 03일</h1>
<div className="day">수요일</div>
<div className="tasks-left">할일이 {undoneTasks.length}개 남았소</div>
</TodoHeadBlock>
);
}
export default TodoHead;

today 설정
Date()와 toLocalDateSring을 사용해보자
import React from "react";
import styled from "styled-components";
import {useTodoState} from "./TodoContext";
const TodoHeadBlock = styled.div`
padding-top: 48px;
padding-left: 32px;
padding-right: 24px;
padding-left: 32px;
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;
}
`;
function TodoHead() {
const todos = useTodoState();
const undoneTasks = todos.filter(todo => !todo.done);
const today = new Date();
const dateString = today.toLocaleDateString('ko-KR', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
const dayName = today.toLocaleDateString('ko-KR', {weekday: 'long'});
return (
<TodoHeadBlock>
<h1>{dateString}</h1>
<div className="day">{dayName}</div>
<div className="tasks-left">할일이 {undoneTasks.length}개 남았소</div>
</TodoHeadBlock>
);
}
export default TodoHead;

TodoList
state 를 조회, 렌더링
import React from "react";
import styled from "styled-components";
import TodoItem from "./TodoItem";
import {useTodoState} from "./TodoContext";
const TodoListBlock = styled.div`
// flex: 1; 자신의 영역을 꽉채우도록 설정
flex: 1;
padding-top: 20px;
padding-left: 32px;
padding-bottom: 48px;
padding-right: 32px;
overflow-y: auto;
`;
function TodoList() {
const todos = useTodoState();
return (
<TodoListBlock>
{todos.map(todo =>
<TodoItem
key={todo.id}
id={todo.id}
text={todo.text}
done={todo.done}
/>
)}
</TodoListBlock>
);
}
export default TodoList;
하드코딩 버리고 map()
TodoItem
dispatch 사용, 토글 / 삭제 기능 구현
import React from "react";
import styled, {css} from "styled-components";
import {MdDelete, MdDone} from "react-icons/md";
import {useTodoDispatch} from "./TodoContext";
// 휴지통 아이콘
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}) {
const dispatch = useTodoDispatch();
const onToggle = () => dispatch({ type: 'TOGGLE', id});
const onRemove = () => dispatch({ type: 'REMOVE', id});
return(
<TodoItemBlock>
<CheckCircle done={done} onClick={onToggle}>{done && <MdDone />}</CheckCircle>
<Text done={done}>{text}</Text>
<Remove onClick={onRemove}>
<MdDelete />
</Remove>
</TodoItemBlock>
);
}
export default TodoItem;

export default React.memo(TodoItem);
잘 작동되면 React.memo 사용하자, 불필요한 렌더링 방지.
TodoCreate
import React, {useState} from "react";
import styled, {css} from "styled-components";
import {MdAdd} from "react-icons/md";
import {useTodoDispatch, useTodoNextId} from "./TodoContext";
const CircleButton = styled.div`
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.div`
background: #f8f9fa;
padding-top: 32px;
padding-right: 32px;
padding-bottom: 72px;
padding-left: 32px;
border-bottom-right-radius: 16px;
border-bottom-left-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 [value, setValue] = useState('');
const dispatch = useTodoDispatch();
const nextId = useTodoNextId();
const onToggle = () => setOpen(!open);
const onChange = e => setValue(e.target.value);
const onSubmit = e => {
// 새로고침 방지
e.preventDefault();
dispatch({
type: 'CREATE',
todo: {
id: nextId.current,
text: value,
done: false
}
});
setValue('');
setOpen(false);
nextId.current += 1;
};
return (
<>
{open && (
<InsertFormPositioner>
<InsertForm >
<Input
autoFocus
placeholder="할 일을 입력 후, Enter 를 누르시게"
onChange={onChange}
value={value}
/>
<button type={"submit"} onClick={onSubmit}>매우당황</button>
</InsertForm>
</InsertFormPositioner>
)}
<CircleButton onClick={onToggle} open={open}>
<MdAdd />
</CircleButton>
</>
);
}
export default React.memo(TodoCreate);
원래라면 엔터를 누르면 ? 입력이 되도록해야하는데
무엇인가 잘못됐나보다..
당황하지 않고? 걍 간단하게 버튼을 누르면 입력되도록 바꾸자

728x90
반응형
'JS Library > React' 카테고리의 다른 글
| React Router (0) | 2022.08.04 |
|---|---|
| Context API 활용 상태 관리 (0) | 2022.08.03 |
| todolist 만들기 (0) | 2022.08.03 |
| styled-components (0) | 2022.08.02 |
| react-icons link (0) | 2022.08.01 |
| CSS Module (0) | 2022.08.01 |