Jieunny의 블로그
TodoList ( react + style-component) 본문
- App.js
-
import logo from './logo.svg'; // import './App.css'; import styled from 'styled-components'; import Template from './components/Template'; import TodoList from './components/TodoList'; import TodoInsert from './components/TodoInsert'; import { useState } from 'react'; import { MdAddCircle } from 'react-icons/md'; let nextId = 4; // 밖에다 선언해놔야 리렌더링해도 4로 돌아가지 않음 function App() { const [selectedTodo, setSelectedTodo] = useState(''); const [insertToggle, setInsertToggle] = useState(false); const [todos, setTodos] = useState([ { id: 1, text: '할일 1', checked: true }, { id: 2, text: '할일 2', checked: false }, { id: 3, text: '할일 3', checked: false } ]); function onInsertToggle(){ if (selectedTodo){ setSelectedTodo(null); } setInsertToggle((prev)=>!prev); } function onInsertTodo(text){ if(text === ''){ alert('할일을 입력해주세요'); } else{ const todo = { id: nextId, text: text, checked : false } setTodos(todos.concat(todo)); nextId++; } } function onCheckToggle(id){ setTodos(todos.map((todo)=>{ return( todo.id === id ? {...todo, checked: !todo.checked} : todo ); })) } function onChangeSelectedTodo(todo){ setSelectedTodo(todo); } function onRemove(id){ onInsertToggle(); setTodos(todos.filter(todo => todo.id !== id)) } function onUpdate(id, text){ onInsertToggle(); setTodos(todos.map((todo)=>{ return( todo.id === id ? {...todo, text} : todo ); })) } return ( <div> <Template todos={todos}> <TodoList todos={todos} onCheckToggle={ onCheckToggle } onInsertToggle={ onInsertToggle } onChangeSelectedTodo={ onChangeSelectedTodo } /> <AddTodoButton onClick={onInsertToggle}> <MdAddCircle /> </AddTodoButton> { insertToggle ? <TodoInsert onInsertToggle={ onInsertToggle } onInsertTodo={ onInsertTodo } selectedTodo={ selectedTodo } onRemove={ onRemove } onUpdate={ onUpdate } /> : null } </Template> </div> ); } const AddTodoButton = styled.div` position: fixed; right: 10px; bottom: 20px; font-size: 5rem; color: pink; `; export default App;
-
- Template.js
-
import React from 'react'; import TodoList from './TodoList'; // import './Template.css' import styled from 'styled-components'; function Template({ children, todos }){ return( <TodoTemplate> <Title>오늘의 할일 ({todos.length})</Title> { children } </TodoTemplate> ); } const TodoTemplate = styled.div` width: 80%; z-index: 50; border: 1px solid pink; border-radius: 5px; padding: 20px; margin: auto; margin-top: 50px; text-align: center; `; const Title = styled.h2` margin: 0; padding: 0; color: rgb(184, 130, 211); `; export default Template;
-
- TodoList.js
-
import React from 'react'; // import './TodoList.css' import TodoItem from './TodoItem'; import styled from 'styled-components'; import TodoInsert from './TodoInsert'; function TodoList({ todos, onCheckToggle, onInsertToggle, onChangeSelectedTodo }){ return ( <Todolist> {todos.map((todo,i)=>{ return <TodoItem key={i} todo={todo} onCheckToggle={ onCheckToggle } onInsertToggle={ onInsertToggle } onChangeSelectedTodo={ onChangeSelectedTodo } /> })}</Todolist> ) } const Todolist = styled.div` margin-top: 20px; text-align: left; `; export default TodoList;
-
- TodoItem.js
-
import React from "react"; import { MdCheckCircle, MdCheckBoxOutlineBlank } from 'react-icons/md' // import './TodoItem.css' import styled, { css } from 'styled-components'; function TodoItem({ todo, onCheckToggle, onInsertToggle, onChangeSelectedTodo }){ const { id, text, checked } = todo; // 객체 구조 분해 할당 return ( <Item> <Check checked={checked}> {/* <div className={`content ${checked ? "checked" : ""}`}> */} { checked ? < MdCheckCircle onClick={()=>{onCheckToggle(id)}} /> : < MdCheckBoxOutlineBlank onClick={()=>{onCheckToggle(id)}} />} <Text onClick={()=>{ onInsertToggle() onChangeSelectedTodo(todo); }}><Content>{ text }</Content></Text> </Check> </Item> ); } const Item = styled.div` border: 1px solid pink; border-radius: 5px; padding: 10px; box-shadow: 5px 3px 5px 0.5px pink; margin-bottom: 10px; `; const Text = styled.div` display: inline-block; vertical-align: middle; cursor: pointer; `; const Content = styled.p` `; const Check = styled.div` svg{ display: inline-block; padding: 5px; vertical-align: middle; color: hotpink; cursor: pointer; } p { ${({ checked }) => { return checked ? css` text-decoration : line-through; opacity : 0.5; ` : null; } } `; export default TodoItem;
-
- TodoInsert.js
-
import React, { useEffect, useState } from 'react'; import { MdAddCircle } from 'react-icons/md'; import { TiPencil, TiTrash } from 'react-icons/ti'; import styled from 'styled-components'; // import './TodoInsert.css'; function TodoInsert({ onInsertToggle, onInsertTodo, selectedTodo, onRemove, onUpdate }){ const [value, setValue] = useState(''); const onChange = (e)=>{ setValue(e.target.value); }; function onSubmit(e){ e.preventDefault(); onInsertTodo(value); setValue(''); onInsertToggle(); }; useEffect(()=>{ if (selectedTodo){ setValue(selectedTodo.text); } }, [selectedTodo]); return( <div> <Background onClick={ onInsertToggle } /> <Form onSubmit={selectedTodo ? ()=>{ onUpdate(selectedTodo.id, value) }: onSubmit }> <Input placeholder="할일을 입력해주세요." onChange={ onChange } value={ value } ></Input> {selectedTodo ? ( <div className="rewrite"> <TiPencil className="pencil" onClick={()=>{ onUpdate(selectedTodo.id, value); }}/> <TiTrash className="trash" onClick={()=>{ onRemove(selectedTodo.id); }} /> </div> ) : ( <Submit type="submit"><MdAddCircle /></Submit> ) } </Form> </div> ); } const Background = styled.div` position: fixed; z-index: 200; top: 0; bottom: 0; left: 0; right: 0; background: rgb(162, 123, 181); width: 100vw; height: 100vh; opacity: 0.8; cursor: pointer; align-items: center; `; const Form = styled.form` position: fixed; top: 40%; left: 10%; z-index: 300; display: flex; flex-direction: column; justify-content: center; align-items: center; width: 300px; height: 150px;; background: white; border-radius: 5px; box-shadow: 3px 3px 3px 0.5px pink; .pencil, .trash{ font-size: 1.5rem; color: pink; padding-top: 20px; padding-left: 10px; padding-right: 20px; } `; const Input = styled.input` border: 0; border-bottom: 1px solid rgb(162, 123, 181); background: inherit; padding: 5px; width: 210px; font-size: 1.3rem; margin-top: 10px; text-align: left; outline: none; `; const Submit = styled.button` border: 0; background: inherit; color: pink; font-size: 1.5rem; margin-top: 15px; `; export default TodoInsert;
-
- TodoItem.js 에서 checked 클래스이름을 조건부로 전달하는 부분을 styled-component로 바꾸는 데에서 막혔었다.
- CSS 속성이 아닌 다른 Props 를 넘길때에는 다른 방법을 이용해야한다.