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 />
},
{
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'
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>
<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
컴포넌트를 이용하여 수정 페이지 폼을 만들어 보자.
EditPage
의 BlogForm
은 Create a blog post 문구 대신 Edit a Blog post와 Post 버튼 대신 Edit 버튼을 출력해야 한다.
- props로 editing 이라는 boolean 값을 전달하고, 값에 따라 다른 문구와 버튼을 출력하도록 삼항 연산자를 이용하여 작성한다.
- BlogForm에서 editing 이라는 props의 type를 boolean으로, 기본 값은 false로 정의
EditPage
의 BlogForm
은 저장된 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';
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 {id} = useParams();
useEffect(() => {
if (editing) {
axios.get(`http://localhost:3001/posts/${id}`)
.then((response) => {
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>
)
}
BlogForm.propTypes = {
editing: bool
}
BlogForm.defaultProps = {
editing: false
}
export default BlogForm
- 현재
BlogForm
의 onSubmit
함수는 새로운 게시글을 생성하도록 작성되어 있음
- 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';
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('');
const {id} = useParams();
useEffect(() => {
if (editing) {
axios.get(`http://localhost:3001/posts/${id}`)
.then((response) => {
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