React-4 댓글창 CRUD (23/02/24)

nazzzo·2023년 2월 24일
0
post-custom-banner

import React, { useState, useEffect, useRef } from "react";
import axios from "axios";



const CommentForm = (props) => {
    const [inputValue, newInputValue] = useState("")

    const changeHandler = (e) => {
        newInputValue(e.target.value)
    }

    const submitHandler = (e) => {
        e.preventDefault()
        props.create(inputValue)
        newInputValue("")
        e.target.commentInput.focus()
    }
    return (
        <li className="comment-form">
            <form onSubmit={submitHandler}>
                <h4>댓글 쓰기
                    <span> ({props.length})</span>
                </h4>
                <span className="ps_box">
                    <input 
                    type="text" 
                    onChange={changeHandler} 
                    className='int' 
                    id="commentInput" 
                    value={inputValue}
                    placeholder="댓글 내용을 입력해주세요" 
                    />
                </span>
                <input type="submit" value="등록" className="btn" />
            </form>
        </li>
    )
}

const CommentItem = (props) => {
    const [liMode, editMode] = useState(false)
    const [content, newContent] = useState(props.content)
    const inputRef = useRef()
    // console.log(props)

    useEffect(() => {
        if (liMode) {
            inputRef.current.focus();
        }
    }, [liMode]);

    const modifyHandler = (e) => {
        editMode(true)
    }
    const changeHandler = (e) => {
        newContent(e.target.value)
    }
    const keyPressHandler = async (e) => {
        if (e.keyCode === 13) {
            const updatedData = e.target.value
            await props.modify(updatedData, props.id)
            editMode(false)
        }
        else if (e.keyCode === 27) editMode(false)
    }

    const deleteHandler = () => {
        props.destroy(props.id)
    }

    return (
        <ul className="comment-row">
            <li className="comment-id">{props.userid}</li>
            {
                liMode
                ? <input type="text" value={content} onChange={changeHandler} ref={inputRef} onKeyDown={keyPressHandler} />  
                : <li className="comment-content" onClick={modifyHandler}>{props.content}</li>  
            }
            <li className="comment-date">{props.date}</li>
            <li className="delete-comment" onClick={deleteHandler}>🆇</li>
        </ul>
    )
}


const CommentList = (props) => {
    const renderList = (v, idx) => {
        return <CommentItem key={idx} id={v.id} userid={v.userid} content={v.content}  date={v.createdAt.split("T")[0]} destroy={props.destroy} modify={props.modify} />
    }

    return (
        <li>
            {props.list.map(renderList)}
        </li>
    )
}


const Comment = () => {
    const [list, newList] = useState([])

    const getList = async () => {
        const response = await axios.get("http://localhost:3000/comments")
        newList(response.data)
    }
    useEffect(()=> {
        getList()
    }, [])

  
    // 댓글 생성
    const create = async (inputValue) => {
        const response = await axios.post("http://localhost:3000/comments", {content : inputValue})
        newList([response.data, ...list])
    }

    // 댓글 수정
    const modify = async (content, id) => {
        // console.log(content, id)
        const response = await axios.put(`http://localhost:3000/comments/${id}`, { content })
        if (response.data === 1) {
            const updatedList = list.filter(v => v.id !== id)
            newList(updatedList)
        }
    }

    // 댓글 삭제
    const destroy = async (id) => {
        const response = await axios.delete(`http://localhost:3000/comments/${id}`)
        if (response.data === 1) {
            const updatedList = list.filter(v => v.id !== id)
            newList(updatedList)
        }
    }


    return (
        <ul className="comment">
            <CommentForm create={create} length={list.length} />
            <CommentList modify={modify} destroy={destroy} list={list} />
        </ul>
    )
}


export default Comment

(23/02/28 수정)

  • 여기서 핵심은 최상위 컴포넌트 역할을 하는 <Comment />입니다
    Comment 컴포넌트가 수정 및 삭제 후의 상태 관리가 가능하도록 설계해야 합니다
    (create, modify, destroy 함수를 상위 컴포넌트에 미리 생성하고 props를 통해서 하위 컴포넌트로 전달하기.
    함수도 값으로 취급하기 때문에 함수를 프로퍼티로 전달하는 것이 가능합니다)

  • 댓글 수정 로직을 바꿨습니다
    상태를 추가하고 addEventListener 대신 keypress 이벤트 함수를 따로 만드는 방식으로

  • 수정 및 삭제 결과를 재렌더링할 때는 변경된 부분만을 다시 그리기 위해 filter 메서드를 사용했습니다
    list 배열에서 v.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듭니다



↓ 혹시 몰라서 css도 같이 첨부...

* {
    margin: 0;
    padding: 0;
}
body {
    font-family: "Noto Sans KR", sans-serif;
    font-weight: 300;
}
ul,
li {
    list-style: none;
}

.comment {
    display: flex;
    flex-direction: column;
    flex-wrap: nowrap;
    padding: 30px;
    width: 600px;
    margin: 0 auto;
}

.comment > li {
    margin-top: 20px;
}
.comment > li:nth-child(1) {
    margin: 0px;
}

.comment-row {
    display: flex;
    justify-content: space-between;
    flex-direction: row;
}

.comment-row {
    margin-top: 20px;
    width: 100%;
}

.comment-row > li:nth-child(2) {
    flex-shrink: 0;
    flex-grow: 1;
    padding-left: 25px;
    z-index: 1;
    width: 100%;
}

.comment-row > li:nth-child(2) {
    width: 85px;
}

.comment-form > form {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: space-between;
}

.comment-form > form > h4 {
    width: 100%;
    margin: 14px 0 14px 0;
}

.comment-content {
    word-break: break-all;
    padding-right: 25px;
    position: relative;
}
.comment-content input {
    position: absolute;
    left: 25px;
    top: 0;
    width: 90%;
    height: 100%;
    outline: none;
}


.ps_box {
    display: block;
    position: relative;
    width: 80%;
    height: 51px;
    border: solid 1px #dadada;
    padding: 10px 14px 10px 14px;
    background: #fff;
    box-sizing: border-box;
}

.ps_box > input {
    outline: none;
}

.int {
    display: block;
    position: relative;
    width: 100%;
    height: 29px;
    padding-right: 25px;
    line-height: 29px;
    border: none;
    background: #fff;
    font-size: 15px;
    box-sizing: border-box;
    z-index: 10;
}

.btn {
    width: 18%;
    padding: 18px 0 16px;
    text-align: center;
    box-sizing: border-box;
    text-decoration: none;
    border: none;
    background: #333;
    color: #fff;
    font-size: 14px;
}

.comment-delete-btn {
    display: inline-block;
    margin-left: 7px;
    cursor: pointer;
}

.comment-update-input {
    border: none;
    border-bottom: 1px solid #333;
    font-size: 16px;
    color: #666;
    outline: none;
}
post-custom-banner

0개의 댓글