본문 바로가기

React + React Native + Expo

[React] 리액트 CRUD

반응형

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 

 

반응형