App.jsx > TodoList > TodoForm, Todo.jsx
TodoList - state : todos ([])
TodoForm - state: input ('')
Todo - state : edit ({id:null, value:''})
1. 기본 세팅
https://blckchainetc.tistory.com/259
[80일차 복습] React 리액트, 웹팩, CSS 연결, 반응형 / Header 만들기
기본 세팅 어제 만든 webpack 환경 설정 을 바탕으로 아래 파일들을 복사해서 새로운 파일에 넣기! 1. package.josn 2. webpack.config.js 그리고 새로 아래의 파일들을 만들어 기본 코드 작성하기 3. index..
blckchainetc.tistory.com
아주 유용한 es7 프로그램!! 설치
2. components > Todo.jsx , TodoForm.jsx, TodoList.jsx 생성 +
rfce
입력 + 엔터 -> 자동 기본 코드 생성
3. TodoForm 부터 틀 만들어서 App.js에 연결
import React, {useState} from 'react'
function TodoForm() {
const [input,setInput] = useState('')
return (
<form className="todo-form">
<input
type="text"
placeholder="Add a todo"
value={input}
name="text"
className="todo-input"
/>
<button className="todo-button">Add</button>
</form>
)
}
export default TodoForm
* 버튼을 눌렀을 때 url reloading 방지
const handleSubmit=e=>{
e.preventDefault()
}
return (
<form className="todo-form" onSubmit={handleSubmit}>
* 이건 아직 무슨 말인지 모르겟다 -> props.~ 부분은 일단 주석처리
=> 나중에 받을 props 안의 onSubmit 이라는 함수를 사용하기 위함
* random number 로 id 설정
function TodoForm(props) {
const [input,setInput] = useState('')
const handleSubmit=e=>{
e.preventDefault()
props.onSubmit({
id:Math.floor(Math.random() * 10000),
text:input
})
}
*input - onChange 추가
const handleChange=e=>{
setInput(e.target.value)
}
return (
<form className="todo-form" onSubmit={handleSubmit}>
<input
type="text"
placeholder="Add a todo"
value={input}
name="text"
className="todo-input"
onChange={handleChange}
/>
TodoForm 전체 코드
import React, {useState} from 'react'
function TodoForm(props) {
const [input,setInput] = useState('')
const handleSubmit=e=>{
e.preventDefault() //url 방지
// props.onSubmit({
// id:Math.floor(Math.random() * 10000),
// text:input
// })
setInput('') // 다시 input box reset
}
const handleChange=e=>{
setInput(e.target.value)
}
return (
<form className="todo-form" onSubmit={handleSubmit}>
<input
type="text"
placeholder="Add a todo"
value={input}
name="text"
className="todo-input"
onChange={handleChange}
/>
<button className="todo-button">Add</button>
</form>
)
}
export default TodoForm
4. TodoList
TodoForm 을 list에 담아 App에 출력하기
TodoList.jsx
import React,{useState} from 'react'
import TodoForm from './TodoForm'
function TodoList() {
const [todos, setTodos] = useState([])
return (
<div>
<h1>What's the plan for today? </h1>
<TodoForm/>
</div>
)
}
export default TodoList
TodoForm < TodoList < App
TodoList.jsx - addTodo 함수 생성(new todo list만드는) -> TodoForm으로 보내기
import React,{useState} from 'react'
import TodoForm from './TodoForm'
function TodoList() {
const [todos, setTodos] = useState([])
// 만약에 빈칸을 add하면 빈칸 무시하기
const addTodo = todo =>{
if(!todo.text || /^\s*$/.test(todo.text)){
return
}
console.log('todo=',todo, 'todos=',todos)
const newTodos = [todo, ...todos]
console.log(newTodos)
setTodos(newTodos)
console.log(todo,...newTodos);
}
return (
<div>
<h1>What's the plan for today? </h1>
<TodoForm onSubmit={addTodo}/>
</div>
)
}
export default TodoList
TodoForm.jsx
const handleSubmit=e=>{
e.preventDefault() //url 방지
props.onSubmit({
id:Math.floor(Math.random() * 10000),
text:input
})
setInput('') // 다시 input box reset
}
5. Todo.jsx
import React, {useState} from 'react'
import TodoForm from './TodoForm'
import {RiCloseCircleLine} from 'react-icons/ri'
import {TiEdit} from 'react-icons/ti'
function Todo({todos, completeTodo}) {
const [edit, setEdit] = useState({
id:null,
value:'',
})
console.log('todos', todos)
return todos.map((todo,index)=>(
<div
className={todo.isComplete ? 'todo-row complete' : 'todo-row'}
key={index}
>
<div key={todo.id} onClick={()=>completeTodo(todo.id)}>
{todo.text}
</div>
<div className="icons">
<RiCloseCircleLine />
<TiEdit/>
</div>
</div>
))
}
export default Todo
icons 다운
npm i react-icons
TodoList - Todo 추가
import Todo from './Todo'
.
.
.
return (
<div>
<h1>What's the plan for today? </h1>
<TodoForm onSubmit={addTodo}/>
<Todo
todos={todos}
completeTodo={completeTodo}
/>
</div>
)
}
--------------------------------여기까지 CRUD의 CREATE -----------------------------
6. Delete 삭제
Todo.jsx
removeTodo, setEdit 추가
import React, {useState} from 'react'
import TodoForm from './TodoForm'
import {RiCloseCircleLine} from 'react-icons/ri'
import {TiEdit} from 'react-icons/ti'
function Todo({todos, completeTodo, removeTodo}) { //추가 0
const [edit, setEdit] = useState({
id:null,
value:'',
})
console.log('todos', todos)
return todos.map((todo,index)=>(
<div
className={todo.isComplete ? 'todo-row complete' : 'todo-row'}
key={index}
>
<div key={todo.id} onClick={()=>completeTodo(todo.id)}>
{todo.text}
</div>
<div className="icons">
<RiCloseCircleLine
onClick={()=>removeTodo(todo.id)} // 추가 1
className="delete-icon"
/>
<TiEdit
onClick={()=>setEdit({id:todo.id,value:todo.text})} // 추가 2
className="edit-icon"
/>
</div>
</div>
))
}
export default Todo
TodoList.jsx
const completeTodo = id=>{ //추가 1
let updatedTodos = todos.map(todo=>{
if (todo.id === id){
todo.isComplete = !todo.isComplete
}
return todo
})
setTodos(updatedTodos)
}
return (
<div>
<h1>What's the plan for today? </h1>
<TodoForm onSubmit={addTodo}/>
<Todo
todos={todos} // 추가 2
completeTodo={completeTodo}
removeTodo={removeTodo}
/>
</div>
)
}
7. Update 수정
TodoList.jsx
const updateTodo = (todoId, newValue)=>{ //추가 1
if(!newValue || /^\s*$/.test(newVlaue)){
return
}
setTodos(prev => prev.map(item=>(item.id===todoId ? newValue : item)))
}
return (
<div>
<h1>What's the plan for today? </h1>
<TodoForm onSubmit={addTodo}/>
<Todo
todos={todos}
completeTodo={completeTodo}
removeTodo={removeTodo}
updateTodo={updateTodo} //추가 2
/>
</div>
)
}
export default TodoList
Todo
import React, {useState} from 'react'
import TodoForm from './TodoForm'
import {RiCloseCircleLine} from 'react-icons/ri'
import {TiEdit} from 'react-icons/ti'
function Todo({todos, completeTodo, removeTodo, updateTodo}) {
const [edit, setEdit] = useState({
id:null,
value:'',
})
const submitUpdate=value=>{
updateTodo(edit.id, value)
setEdit({
id:null,
value:'',
})
}
if (edit.id) {
return <TodoForm edit={edit} onSubmit={submitUpdate} />
}
return todos.map((todo,index)=>(
<div
className={todo.isComplete ? 'todo-row complete' : 'todo-row'}
key={index}
>
<div key={todo.id} onClick={()=>completeTodo(todo.id)}>
{todo.text}
</div>
<div className="icons">
<RiCloseCircleLine
onClick={()=>removeTodo(todo.id)}
className="delete-icon"
/>
<TiEdit
onClick={()=>setEdit({id:todo.id,value:todo.text})}
className="edit-icon"
/>
</div>
</div>
))
}
export default Todo
todo Form 의 props.Submit 함수는 todoList & Todo 둘 다에게로 부터 onSubmit이라는 함수를 받고 수정 시에만 todo의 onSubmit으로 된다.
props.onSubmit({
id:Math.floor(Math.random() * 10000),
text:input,
// isComplete:false
})
8. cursor
TodoForm.jsx
useEffect, useRef 추가
import React, {useState, useEffect, useRef} from 'react'
function TodoForm(props) {
const [input,setInput] = useState('')
const inputRef=useRef(null)
useEffect(()=>{
inputRef.current.focus()
})
.
.
.
<form className="todo-form" onSubmit={handleSubmit}>
<input
type="text"
placeholder="Add a todo"
value={input}
name="text"
className="todo-input"
onChange={handleChange}
ref={inputRef} // ref 추가
/>
자동으로 cursor가 input box 안에 생김
9. CSS
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande',
'Lucida Sans', Arial, sans-serif;
}
body {
background: linear-gradient(
90deg,
rgba(48, 16, 255, 1) 0%,
rgba(100, 115, 255, 1) 100%
);
}
.todo-app {
display: flex;
flex-direction: column;
justify-content: start;
width: 520px;
min-height: 600px;
background: #161a2b;
text-align: center;
margin: 128px auto;
border-radius: 10px;
padding-bottom: 32px;
}
h1 {
margin: 32px 0;
color: #fff;
font-size: 24px;
}
.complete {
text-decoration: line-through;
opacity: 0.4;
}
.todo-form {
margin-bottom: 32px;
}
.todo-input {
padding: 14px 32px 14px 16px;
border-radius: 4px 0 0 4px;
border: 2px solid #5d0cff;
outline: none;
width: 320px;
background: transparent;
color: #fff;
}
.todo-input::placeholder {
color: #e2e2e2;
}
.todo-button {
padding: 16px;
border: none;
border-radius: 0 4px 4px 0;
cursor: pointer;
outline: none;
background: linear-gradient(
90deg,
rgba(93, 12, 255, 1) 0%,
rgba(155, 0, 250, 1) 100%
);
color: #fff;
text-transform: capitalize;
}
.todo-input.edit {
border: 2px solid #149fff;
}
.todo-button.edit {
background: linear-gradient(
90deg,
rgba(20, 159, 255, 1) 0%,
rgba(17, 122, 255, 1) 100%
);
padding: 16px 22px;
}
.todo-container {
display: flex;
flex-direction: row;
position: relative;
}
.todo-row {
display: flex;
justify-content: space-between;
align-items: center;
margin: 4px auto;
color: #fff;
background: linear-gradient(
90deg,
rgba(255, 118, 20, 1) 0%,
rgba(255, 84, 17, 1) 100%
);
padding: 16px;
border-radius: 5px;
width: 90%;
}
.todo-row:nth-child(4n + 1) {
background: linear-gradient(
90deg,
rgba(93, 12, 255, 1) 0%,
rgba(155, 0, 250, 1) 100%
);
}
.todo-row:nth-child(4n + 2) {
background: linear-gradient(
90deg,
rgba(255, 12, 241, 1) 0%,
rgba(250, 0, 135, 1) 100%
);
}
.todo-row:nth-child(4n + 3) {
background: linear-gradient(
90deg,
rgba(20, 159, 255, 1) 0%,
rgba(17, 122, 255, 1) 100%
);
}
.icons {
display: flex;
align-items: center;
font-size: 24px;
cursor: pointer;
}
.delete-icon {
margin-right: 5px;
color: #fff;
}
.edit-icon {
color: #fff;
}
10. 기타 수정
todoform
function TodoForm(props) {
const [input,setInput] = useState(props.edit ? props.edit.value : '')
전체 코드
import React, { useState, useEffect, useRef } from 'react'
function TodoForm(props) {
const [input, setInput] = useState(props.edit ? props.edit.value : '')
const inputRef = useRef(null)
useEffect(() => {
inputRef.current.focus()
})
const handleSubmit = e => {
e.preventDefault() //url 방지
props.onSubmit({
id: Math.floor(Math.random() * 10000),
text: input,
})
setInput('') // 다시 input box reset
}
const handleChange = e => {
setInput(e.target.value)
}
return (
<form className="todo-form" onSubmit={handleSubmit}>
{props.edit ? (
<>
<input
type="text"
placeholder="update your item"
value={input}
name="text"
className="todo-input edit"
onChange={handleChange}
ref={inputRef}
/>
<button className="todo-button edit">Add</button>
</>
) : (
<>
<input
type="text"
placeholder="Add a todo"
value={input}
name="text"
className="todo-input"
onChange={handleChange}
ref={inputRef}
/>
<button className="todo-button">Add</button>
</>
)}
</form>
)
}
export default TodoForm
reference: https://www.youtube.com/watch?v=E1E08i2UJGI
'React + React Native + Expo' 카테고리의 다른 글
[React Native] Bottom tab에 아이콘 추가하기 expo/vector-icons (0) | 2021.08.04 |
---|---|
[React Native expo] chatting app 만들기 tutorial (0) | 2021.08.02 |
[React native] 기초 다지기 - view / 버튼 / Image / flex 세로 가로 설정 / 정렬 등 CSS (0) | 2021.07.30 |
[React 연습 5일차] 리액트 TicTacToe Game 구현 with Hooks API (0) | 2021.07.14 |
[React 연습 4일차] 리액트 TicTacToe Game 구현 (0) | 2021.07.05 |