[React] redux를 이용하여 게시판 만들기(3) - 떽떽대는 개발공부
2021/01/26 - [React] - [React] redux를 이용하여 게시판 만들기(2) - 떽떽대는 개발공부
지난 글에서 글 목록 클릭 시 해당 글 상세보기 페이지 구현하기를 하였다.
이번 글에서는 상세보기 페이지에서 글 수정 및 삭제 기능을 추가 할 것이다.
src/components/BoardContent.js
import React, { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux'
import { editContent, removeContent } from '@modules/boardReducer'
import { useHistory } from 'react-router-dom';
function BoardContent() {
const { selectRowData } = useSelector(state => state.boardReducer)
// input 값을 변경할 수 있도록 제어
const [title, setTitle] = useState(selectRowData.title)
const [content, setContent] = useState(selectRowData.content)
// input 값 변경될때마다 값을 변경
const handleTitle = (e) => {
setTitle(e.target.value)
}
const handleContent = (e) => {
setContent(e.target.value)
}
// 함수형 컴포넌트에서 useDispatch를 사용할 수 있도록 선언
const dispatch = useDispatch()
// 함수형 컴포넌트에서 useHistory 를 사용할 수 있도록 선언
const history = useHistory();
const onChange = () => {
// reducer의 data와 동일한 형식으로 맞춰준다.
const _inputData = {
id: selectRowData.id,
title: title,
content: content
}
// reducer 의 edirContent 함수에 변경값 전달
dispatch(editContent(_inputData))
// input 란 초기화
setTitle('')
setContent('')
// BoardList 페이지로 이동
history.push('/')
}
return(
<div>
<h2>상세보기</h2>
<div>
<div>
<input type='text' className='inputTitle' onChange={handleTitle} value={title} />
</div>
<div>
<textarea className='inputContent' onChange={handleContent} value={content} />
</div>
<div>
<button type='button' onClick={onChange} className='editBtn'>edit</button>
<button type='button' className='deleteBtn'>delete</button>
</div>
</div>
</div>
)
}
export default BoardContent;
먼저 BoardContent 에서 input 란 수정할 수 있도록 value 값을 setTitle, setContent 로 제어 해준다.
input 값이 변경될 때마다 handleTitle, handleContent 함수를 호출하여 setTitle, setContent 로 실시간 값을 변경해 주고, edit 버튼을 클릭하면 onChange 함수를 호출하게 된다.
onChange 함수에서는 boardReducer 의 inputData 의 형식에 맞추어 데이터를 구성하고, editContent 함수를 호출하여 값을 전달한다.
dispatch 완료 후 화면 전환을 위해 useHistory를 사용하여 list 페이지로 이동한다. 페이지 이동 시 새로고침 되면 기존의 데이터가 날아가게 되므로 useHistory 를 사용한다.
이제 boardReducer.js 를 수정해준다.
src/modules/boardReducer.js
const _SAVE = 'DATA_SAVE';
const _SELECT = 'DATA_SELECT'
const _EDIT = 'DATA_EDIT'
export const dataSave = (inputData) => ({
type: _SAVE,
inputData: {
id: inputData.id,
title: inputData.title,
content: inputData.content
}
})
export const selectRow = (id) => (
console.log('reducer :: id :: ', id),
{
type: _SELECT,
inputData: {
id: id,
}
})
// boardContent 에서 받아온 값
export const editContent = (inputData) => ({
type: _EDIT,
inputData: {
id: inputData.id,
title: inputData.title,
content: inputData.content
}
})
const initialState = {
lastId: 0,
inputData: [
{
id: '',
title: '',
content: ''
}
],
selectRowData: {}
}
export default function boardReducer(state = initialState, action){
switch(action.type) {
case _SAVE:
console.log(state.inputData)
return {
lastId: state.lastId + 1,
inputData: state.inputData.concat({
...action.inputData,
id: state.lastId + 1,
})
}
case _SELECT:
console.log(action)
return {
...state,
selectRowData: state.inputData.find(row => row.id === action.inputData.id)
}
case _EDIT:
return {
...state,
// state 에 저장되어 있는 inputData 중 동일한 id 값을 가진 데이터를 action.inputData 값으로 변경해준다.
inputData: state.inputData.map(row =>
row.id === action.inputData.id ?
{...action.inputData} : row
),
selectRowData: {}
}
default:
return state
}
}
이렇게 하면 글 수정하는 것까지 완료 되었다. 마지막으로 글 삭제 부분을 해보자.
src/components/BoardContent.js
import React, { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux'
import { editContent, removeContent } from '@modules/boardReducer'
import { useHistory } from 'react-router-dom';
function BoardContent() {
const { selectRowData } = useSelector(state => state.boardReducer)
const [title, setTitle] = useState(selectRowData.title)
const [content, setContent] = useState(selectRowData.content)
const handleTitle = (e) => {
setTitle(e.target.value)
}
const handleContent = (e) => {
setContent(e.target.value)
}
const dispatch = useDispatch()
const history = useHistory();
const onChange = () => {
const _inputData = {
id: selectRowData.id,
title: title,
content: content
}
console.log('clickSave :: ', _inputData)
dispatch(editContent(_inputData))
setTitle('')
setContent('')
history.push('/')
}
const onRemove = () => {
// reducer의 removeContent 함수에 삭제할 id 값을 전달한다.
dispatch(removeContent(selectRowData.id))
// input 값 초기화
setTitle('')
setContent('')
// 페이지 이동
history.push('/')
}
return(
<div>
<h2>상세보기</h2>
<div>
<div>
<input type='text' className='inputTitle' onChange={handleTitle} value={title} />
</div>
<div>
<textarea className='inputContent' onChange={handleContent} value={content} />
</div>
<div>
<button type='button' onClick={onChange} className='editBtn'>edit</button>
<button type='button' onClick={onRemove} className='deleteBtn'>delete</button>
</div>
</div>
</div>
)
}
export default BoardContent;
delete 버튼 클릭 시 onRemove 함수가 호출되고 reducer 의 removeContent 함수에 dispatch 된다.
이때 전달할 id 는 useSelector 에서 받아온 id 값을 그대로 사용한다.
이제 boardReducer.js 만 에서 추가 수정해주자.
src/modules/boardReducer.js
const _SAVE = 'DATA_SAVE';
const _SELECT = 'DATA_SELECT'
const _EDIT = 'DATA_EDIT'
const _DELETE = 'DATA_DELETE'
export const dataSave = (inputData) => ({
type: _SAVE,
inputData: {
id: inputData.id,
title: inputData.title,
content: inputData.content
}
})
export const selectRow = (id) => (
console.log('reducer :: id :: ', id),
{
type: _SELECT,
inputData: {
id: id,
}
})
export const editContent = (inputData) => (
console.log('reducer :: edit :: ', inputData),
{
type: _EDIT,
inputData: {
id: inputData.id,
title: inputData.title,
content: inputData.content
}
})
// 삭제할 id값을 받아온다.
export const removeContent = (id) => ({
type: _DELETE,
inputData: {
id: id,
}
})
const initialState = {
lastId: 0,
inputData: [
{
id: '',
title: '',
content: ''
}
],
selectRowData: {}
}
export default function boardReducer(state = initialState, action){
switch(action.type) {
case _SAVE:
console.log(state.inputData)
return {
lastId: state.lastId + 1,
inputData: state.inputData.concat({
...action.inputData,
id: state.lastId + 1,
})
}
case _SELECT:
console.log(action)
return {
...state,
selectRowData: state.inputData.find(row => row.id === action.inputData.id)
}
case _EDIT:
return {
...state,
inputData: state.inputData.map(row =>
row.id === action.inputData.id ?
{...action.inputData} : row
),
selectRowData: {}
}
case _DELETE:
return {
// lastId 값이 현재 삭제 요청된 id값과 동일하면 값을 줄여준다.
lastId: state.lastId === action.inputData.id ? state.lastId - 1 : state.lastId,
// filter를 사용하여 state에 있는 값과 action.id 값이 동일하지 않은 값만 return 하여 state 에 저장해준다.
inputData: state.inputData.filter(row =>
row.id !== action.inputData.id
),
selectRowData: {}
}
default:
return state
}
}
이렇게 하면 간단하게 reducer를 이용한 게시판 만들기가 완료 된다.
처음엔 reducer 의 개념도 이해 못했는데 여러 예제를 통해 사용법을 익혀보니 크게 어렵지 않게 만들 수 있게 되어 기쁘다.
순수 함수의 개념에 대해서는 추가적으로 공부가 필요할 것 같다.