components폴더 내부에 TodoList, TodoItem, ToddAdd 컴포넌트를 각각 만들어줍니다.
TodoList.js
import React, { useState, useRef } from 'react'
import TodoItem from '../TodoItem'
import TodoAdd from '../TodoAdd'
function TodoList() {
const [todoData, setTodoData] = useState([
{
id: 1,
date: '2022-07-28',
context: '강의하기',
checked: false,
},
])
const deleteTodo = (id) => {
setTodoData(todoData.filter((todoItem) => todoItem.id !== id))
}
const idRef = useRef(2)
const todoCheckHandler = (id) => {
setTodoData(
todoData.map((itemData) =>
itemData.id === id
? { ...itemData, checked: !itemData.checked }
: itemData,
),
)
}
return (
<div>
<TodoAdd idRef={idRef} todoData={todoData} setTodoData={setTodoData} />
{todoData.map((todoItem) => (
<TodoItem
todoItem={todoItem}
key={todoItem.id}
deleteTodo={() => deleteTodo(todoItem.id)}
todoCheckHandler={() => todoCheckHandler(todoItem.id)}
/>
))}
</div>
)
}
export default TodoList
TodoItem
import React, { useState } from 'react'
function TodoAdd({ idRef, setTodoData, todoData }) {
const [userInput, setUserInput] = useState({
date: '',
context: '',
})
const addTodo = (userInput) => {
setTodoData([
...todoData,
{
id: idRef.current,
date: userInput.date,
context: userInput.context,
checked: false,
},
])
idRef.current += 1
}
const userInputHandler = (e) => {
const { name, value } = e.target
setUserInput({
...userInput,
[name]: value,
})
}
return (
<div>
<input type="date" name="date" onChange={userInputHandler} />
<input name="context" onChange={userInputHandler} />
<button
onClick={() => {
addTodo(userInput)
}}
>
추가하기
</button>
</div>
)
}
export default TodoAdd
TodoAdd
import React, { useState } from 'react'
function TodoAdd({ idRef, setTodoData, todoData }) {
const [userInput, setUserInput] = useState({
date: '',
context: '',
})
const addTodo = (userInput) => {
setTodoData([
...todoData,
{
id: idRef.current,
date: userInput.date,
context: userInput.context,
checked: false,
},
])
idRef.current += 1
}
const userInputHandler = (e) => {
const { name, value } = e.target
setUserInput({
...userInput,
[name]: value,
})
}
return (
<div>
<input type="date" name="date" onChange={userInputHandler} />
<input name="context" onChange={userInputHandler} />
<button
onClick={() => {
addTodo(userInput)
}}
>
추가하기
</button>
</div>
)
}
export default TodoAdd
가장 먼저 해야할 일은 styles폴더 안의 globalStyle.js와 theme.js 적용입니다.
theme,js
컬러파레트의 색상 조합을 이용해 진행하겠습니다.
const palette = {
yellow: '#F9F9C5',
green: '#D9F8C4',
orange: '#FAD9A1',
red: '#F37878',
}
const common = {
flexCenter: `
display: flex;
align-items: center;
justify-content: center;
`,
flexAround: `
display: flex;
align-items: center;
justify-content: space-around;
`,
flexColumnStart: `
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
`,
}
const fontSizes = {
title: '2rem',
subtitle: '1.5rem',
paragraph: '1rem',
}
const theme = {
palette,
common,
fontSizes,
}
export default theme
globalStyle.js
import { createGlobalStyle } from "styled-components";
import BlackHanSans from '../assets/fonts/BlackHanSans-Regular.ttf'
export default createGlobalStyle`
@font-face {
font-family: "BlackHanSans"
src: url(${BlackHanSans}) format('truetype')
}
`
App.js
import { ThemeProvider } from 'styled-components';
import TodoList from './components/TodoList';
import theme from './styles/theme'
import GlobalStyle from './styles/globalStyle';
function App() {
return (
<ThemeProvider theme={theme}>
<GlobalStyle />
<TodoList />
</ThemeProvider>
);
}
export default App;
TodoList
import styled from 'styled-components'
export const TodoContainer = styled.div`
${({ theme }) => theme.common.flexColumnStart};
background-color: ${({ theme }) => theme.palette.red};
border-radius: 10px;
color: white;
width: 50rem;
height: 33rem;
margin: 3rem auto;
font-family: 'BlackHanSans';
`
export const TodoTitle = styled.p`
font-size: ${({ theme }) => theme.fontSizes.subtitle};
`
import * as S from './style'
function TodoList(){
...
return (
<S.TodoContainer>
<TodoAdd idRef={idRef} todoData={todoData} setTodoData={setTodoData} />
{todoData.map((todoItem) => (
<TodoItem
todoItem={todoItem}
key={todoItem.id}
deleteTodo={() => deleteTodo(todoItem.id)}
todoCheckHandler={() => todoCheckHandler(todoItem.id)}
/>
))}
</S.TodoContainer>
)
}
TodoItem
import styled from 'styled-components'
export const ItemContainer = styled.div`
${({ theme }) => theme.common.flexAround};
flex-shrink: 0;
background-color: ${({ theme }) => theme.palette.yellow};
border-radius: 10px;
color: black;
width: 90%;
height: 5rem;
margin: 0.5rem;
font-family: 'NotoSansBold';
`
export const ItemButton = styled.div`
${({ theme }) => theme.common.flexCenter};
border: none;
outline: none;
cursor: pointer;
&:hover {
opacity: 0.5;
}
`
export const ItemText = styled.div`
font-size: ${({ theme }) => theme.fontSizes.paragraph};
width: 30%;
`
TodoAdd
import styled from 'styled-components'
export const AddContainer = styled.div`
${({ theme }) => theme.common.flexAround};
flex-shrink: 0;
color: black;
width: 90%;
margin-bottom: 1rem;
font-family: 'NotoSansBold';
`
export const AddButton = styled.button`
${({ theme }) => theme.common.flexCenter};
border: none;
outline: none;
cursor: pointer;
background-color: ${({ theme }) => theme.palette.green};
height: 2rem;
width: 5rem;
border-radius: 5px;
&:hover {
opacity: 0.5;
}
`
export const AddInput = styled.input`
border: none;
outline: none;
border-radius: 5px;
padding: 0rem 0.5rem;
height: 2rem;
width: 30%;
`
import React, { useState } from 'react'
import * as S from './style'
function TodoAdd({ todoId, todoData, setTodoData }) {
const [userInput, setUserInput] = useState({ date: '', content: '' })
const userInputHandler = (e) => {
const { name, value } = e.target
setUserInput({ ...userInput, [name]: value })
}
const todoAddHandler = (userInput) => {
setTodoData([
...todoData,
{
id: todoId.current,
date: userInput.date,
content: userInput.content,
checked: false,
},
])
todoId.current += 1
}
return (
<S.AddContainer>
<S.AddInput type="date" name="date" onChange={userInputHandler} />
<S.AddInput name="content" onChange={userInputHandler} />
<S.AddButton onClick={() => todoAddHandler(userInput)}>
추가하기
</S.AddButton>
</S.AddContainer>
)
}
export default TodoAdd
$ yarn add react-icons
TodoItem
import React from 'react'
import * as S from './style'
import { GrCheckbox, GrCheckboxSelected } from 'react-icons/gr'
import { AiOutlineCloseCircle } from 'react-icons/ai'
function TodoItem({ todoItem, deleteTodo, todoCheckHandler }) {
const { date, context, checked } = todoItem
return (
<S.ItemContainer>
<S.ItemButton onClick={todoCheckHandler}>
{checked ? <GrCheckboxSelected /> : <GrCheckbox />}
</S.ItemButton>
<S.ItemText>{date}</S.ItemText>
<S.ItemText>{context}</S.ItemText>
<S.ItemButton onClick={deleteTodo}>
<AiOutlineCloseCircle />
</S.ItemButton>
</S.ItemContainer>
)
}
export default TodoItem
TodoItem
export const ItemContainer = styled.div`
opacity: ${({ isChecked }) => (isChecked ? '0.5' : '1')};
`
return (
<S.ItemContainer isChecked={todoItem.checked}>
...
)
TodoList
useEffect(() => {
setSortData(todoData.sort((a, b) => a.checked - b.checked))
}, [todoData])