[React : mini-project] CRUD(4) - Update

문지은·2023년 8월 1일
0

React

목록 보기
19/24
post-thumbnail

Edit Page

Edit Page route 추가

  • 특정 게시글 id의 Edit Page로 이동할 수 있도록 route path 수정

src/routes.js

import HomePage from './pages/HomePage';
import CreatePage from './pages/CreatePage';
import EditPage from './pages/EditPage';
import ListPage from './pages/ListPage';
import DetailPage from './pages/DetailPage';


const routes = [
  {
    path:'/',
    element: <HomePage />
  },
  {
    path:'/blogs',
    element: <ListPage />
  },
  {
    path:'/blogs/create',
    element: <CreatePage />
  },
  // ***************** Edit Page route **************
  {
    path:'/blogs/:id/edit',
    element: <EditPage />
  },
  {
    path:'/blogs/:id',
    element: <DetailPage />
  },
]

export default routes;
  • DetailPage에서 EditPage로 이동할 수 있는 버튼 작성
    • react router의 Link 컴포넌트 사용

src/pages/DetailPage.jsx

import React, { useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'
import axios from 'axios'
import LoadingSpinner from '../components/LoadingSpinner'

// ********** Link 컴포넌트 추가
import { Link } from 'react-router-dom'

function DetailPage() {
  const { id } = useParams();
  const [post, setPost] = useState(null);
  const [loading, setLoading] = useState(true);

  const getPost = (id) => {
    axios.get(`http://localhost:3001/posts/${id}`)
    .then((res) => {
        setPost(res.data)
        setLoading(false)
    })
  }

  useEffect(() => {
    getPost(id)
  }, [id])

  const printDate = (timestamp) => {
    return new Date(timestamp).toLocaleString();
  }

  if (loading) {
    return <LoadingSpinner />
  }
  return (
    <div>
      <div className='d-flex'>
        <h1 className='flex-grow-1'>{post.title}</h1>
        <div>
          // *********** edit 페이지로 이동하는 링크
          <Link 
            className='btn btn-primary'
            to={`/blogs/${id}/edit`}>
            Edit
          </Link>
        </div>
      </div>
        <small class="text-muted">작성 시간 : {printDate(post.createdAt)}</small>
        <br />
        <p>{post.body}</p>
    </div>
  )
}

export default DetailPage
  • 버튼 출력 확인

수정 페이지 폼

  • 이전에 만들었던 BlogForm 컴포넌트를 이용하여 수정 페이지 폼을 만들어 보자.
  • EditPageBlogForm은 Create a blog post 문구 대신 Edit a Blog post와 Post 버튼 대신 Edit 버튼을 출력해야 한다.
    • props로 editing 이라는 boolean 값을 전달하고, 값에 따라 다른 문구와 버튼을 출력하도록 삼항 연산자를 이용하여 작성한다.
    • BlogForm에서 editing 이라는 props의 type를 boolean으로, 기본 값은 false로 정의
  • EditPageBlogForm은 저장된 Title 과 Body를 작성 폼 안에 보여주어야 한다.
    • useEffect와 axios get 요청을 통해 해당 값을 업데이트

src/pages/EditPage.jsx

import React from 'react'
import BlogForm from '../components/BlogForm'


function EditPage() {
  return (
    <div>
      <BlogForm editing={true}/>
    </div>
  )
}

export default EditPage

src/componenets/BlogForm.jsx

import React from 'react'
import { useState } from 'react';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';
import { bool } from 'prop-types';

// Edit Page ****************************
import { useEffect } from 'react';
import { useParams } from 'react-router-dom';
// **************************************

function BlogForm({editing}) {
  const [title, setTitle] = useState('');
  const [body, setBody] = useState('');
  const navigate = useNavigate();

  // Edit Page *******************************
  const {id} = useParams();
  useEffect(() => {
    if (editing) {
      axios.get(`http://localhost:3001/posts/${id}`)
      .then((response) => {
        // console.log(response.data)
        setTitle(response.data.title)
        setBody(response.data.body)
      })
    }
  }, [id, editing])
// **************************************
  
  const onSubmit = () => {
      axios.post('http://localhost:3001/posts', {
      title: title,
      body: body,
      createdAt: Date.now()
      }).then(() => {
        navigate('/blogs')
      })
  }
  return (
    <div>
        <h1>{editing ? 'Edit' : 'Create'} a blog post</h1>
        <div className='mb-3'>
            <label className='form-label'>Title</label>
            <input 
            className='form-control'
            value={title}
            onChange={(event) => {
                setTitle(event.target.value)
            }}
            />
        </div>
        <div className='mb-3'>
            <label className='form-label'>Body</label>
            <textarea 
            className='form-control'
            value={body}
            onChange={(event) => {
                setBody(event.target.value)
            }}
            rows={20}
            />
        </div>
        <button 
            className='btn btn-primary'
            onClick={onSubmit}
            >{editing ? 'Edit' : 'Post'}</button>
    </div>
  )
}

// editing props 추가 
BlogForm.propTypes = {
  editing: bool
}

BlogForm.defaultProps = {
  editing: false
}

export default BlogForm

  • 현재 BlogFormonSubmit 함수는 새로운 게시글을 생성하도록 작성되어 있음
    • editing 상태일 때는 새로운 게시글 생성이 아닌 게시글을 수정하도록 함수 수정
    • 수정 완료 후에는 해당 게시글의 Detail 페이지로 이동

src/componenets/BlogForm.jsx

...

 const onSubmit = () => {
      if (editing) {
        axios.patch(`http://localhost:3001/posts/${id}`, {
          title: title,
          body: body
        }) .then(() => {
          navigate('/blogs')
        })
      } else {
        axios.post('http://localhost:3001/posts', {
        title: title,
        body: body,
        createdAt: Date.now()
        }).then(() => {
          navigate(`/blogs/${id}`)
        })
      }
  }
 
...

export default BlogForm
  • 잘 수정되는 것 확인

  • 사용자가 게시글을 수정했을 때에만 Edit 버튼이 활성화되도록 수정
    • originalTitle, originalBody state를 만들고 isEdited 함수를 통해 내용이 바뀌었는지 boolean 형태로 반환
    • button의 disabled 속성을 통해 isEdited 값에 따라 button 활성/비활성화

src/componenets/BlogForm.jsx

import React from 'react'
import { useState } from 'react';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';
import { bool } from 'prop-types';

// Edit Page
import { useEffect } from 'react';
import { useParams } from 'react-router-dom';

function BlogForm({editing}) {
  const [title, setTitle] = useState('');
  const [body, setBody] = useState('');
  const navigate = useNavigate();

  const [originalTitle, setOriginalTitle] = useState('');
  const [originalBody, setOriginalBody] = useState('');

  // Edit Page
  const {id} = useParams();
  useEffect(() => {
    if (editing) {
      axios.get(`http://localhost:3001/posts/${id}`)
      .then((response) => {
        // console.log(response.data)
        setTitle(response.data.title)
        setOriginalTitle(response.data.title)
        setBody(response.data.body)
        setOriginalBody(response.data.body)
      })
    }
  }, [id, editing])

  const isEdited = () => {
    return title !== originalTitle || body !== originalBody
  }

  const onSubmit = () => {
      if (editing) {
        axios.patch(`http://localhost:3001/posts/${id}`, {
          title: title,
          body: body
        }) .then(() => {
          navigate(`/blogs/${id}`)
        })
      } else {
        axios.post('http://localhost:3001/posts', {
        title: title,
        body: body,
        createdAt: Date.now()
        }).then(() => {
          navigate('/blogs')
        })
      }
  }
  return (
    <div>
        <h1>{editing ? 'Edit' : 'Create'} a blog post</h1>
        <div className='mb-3'>
            <label className='form-label'>Title</label>
            <input 
            className='form-control'
            value={title}
            onChange={(event) => {
                setTitle(event.target.value)
            }}
            />
        </div>
        <div className='mb-3'>
            <label className='form-label'>Body</label>
            <textarea 
            className='form-control'
            value={body}
            onChange={(event) => {
                setBody(event.target.value)
            }}
            rows={10}
            />
        </div>
        <button 
            className='btn btn-primary'
            onClick={onSubmit}
            disabled={editing && !isEdited()}  
            >{editing ? 'Edit' : 'Post'}</button>
    </div>
  )
}

BlogForm.propTypes = {
  editing: bool
}

BlogForm.defaultProps = {
  editing: false
}

export default BlogForm

  • 게시글 작성/수정을 취소하는 버튼 추가
    • 게시글 작성을 취소할 때는 리스트 페이지로, 게시글 수정을 취소할 때는 디테일 페이지로 이동하는 goBack 함수 작성

src/componenets/BlogForm.jsx

...

function BlogForm({editing}) {
  ...

  const goBack = () => {
    if (editing) {
    navigate(`/blogs/${id}`)
    } else {
      navigate('/blogs/')
    }
  }

  return (
    ...
        <button 
          className='btn btn-danger ms-3'
          onClick={goBack}
          >Cancel</button>
    </div>
  )
}

BlogForm.propTypes = {
  editing: bool
}

BlogForm.defaultProps = {
  editing: false
}

export default BlogForm

profile
코드로 꿈을 펼치는 개발자의 이야기, 노력과 열정이 가득한 곳 🌈

0개의 댓글