목차로 돌아가기: binaryjourney.tistory.com/pages/ReactCRUD-create-board-tutorial
렌더링 최적화를 위해 useState로 사용했던
const [TitleValue, setTitleValue] = useState("")
const [ContentValue, setContentValue] = useState("")
요 useState 구문을 useSelector를 이용하여 코드도 줄이고 reducer를 재활용하는 방법과 렌더링 최적화를 실천해보겠다.
useSelector를 이용한다는 것은 redux-toolkit의 createSlice로 만든 slice에 initialState 로 이미 객체변수가 존재함을 전제로 한다.
내가 리팩토링하려는 RegisterPage 를 보면 이미 useSelector로 initialState를 다 가져다 놓은 상태여서 정말 아주 조금의 편집만 하는 정도이다.
// RegisterPage.js
import React, { useEffect, useState } from "react";
import { shallowEqual, useDispatch, useSelector } from "react-redux";
import RegisterOrEdit from "./Sections/RegisterOrEdit";
import { articleActions } from "../../../slice/articleSlice";
function RegisterPage(props) {
const dispatch = useDispatch();
const { id, views, date, editDate, title, content } = useSelector(
(state) => ({
id: state.articleReducers.id,
views: state.articleReducers.views,
date: state.articleReducers.date,
editDate: state.articleReducers.editDate,
title: state.articleReducers.title,
content: state.articleReducers.content,
}),
shallowEqual
);
.
.
.
}
setTitleValue 와 setContentValue를 사용한 메소드를 찾아 지워주고
다음과 같이 바꿔준다.
일단 작성만 해놓고 저장은 하지 말고 잠시만 기다려주길 바란다.
// RegisterPage.js
const onTitleChange = (event) => {
const { name, value } = event.target;
dispatch(articleActions.changeRegisterInput({ name: name, value: value }));
};
const onContentChange = (event) => {
const { name, value } = event.target;
dispatch(articleActions.changeRegisterInput({ name: name, value: value }));
};
그리고 articleActions 를 export 하는 articleSlice로 가서 reducer에 다음 항목을 추가한다.
// articleSlice.js
changeRegisterInput: (state, { payload }) => {
switch (payload.name) {
case "title":
return {
...state,
title: payload.value,
};
case "content":
return {
...state,
content: payload.value,
};
default:
break;
}
},
두 코드블럭을 해석해보면 같은 action 함수에서 name이 title인지 content인지 가려서 onChange 이벤트가 발생했을 때의 value를 state에 반영해준다.
그리고 RegisterPage에서 TitleValue 와 ContentValue가 쓰인 곳을 useSelector로 잡아온 title 과 content로 바꿔준다.
// RegisterPage.js
function RegisterPage(props) {
...
if (title === "" || title === null || title === undefined) { // 수정
alert("제목을 작성하십시오.");
return false;
}
if (content === "" || content === null || content === undefined) { // 수정
alert("내용을 작성하십시오.");
return false;
}
...
if (IsForUpdate) {
dispatch(articleActions.updateArticle(article));
} else {
dispatch(articleActions.registerArticle(article));
}
};
return (
<>
<RegisterOrEdit
titleValue={title} // 수정
contentValue={content} // 수정
handleTitleChange={onTitleChange}
handleContentChange={onContentChange}
handleSubmit={onSubmitArticle}
updateRequest={IsForUpdate}
/>
</>
);
}
RegisterPage 의
const [IsForUpdate, setIsForUpdate] = useState(false);
부분은 initialState에 없기 때문에 지우면 안된다.
이건 필요하다.
더 클린코딩을 해보자면 RegisterPage의
const onTitleChange = (event) => {
const { name, value } = event.target;
dispatch(articleActions.changeRegisterInput({ name: name, value: value }));
};
const onContentChange = (event) => {
const { name, value } = event.target;
dispatch(articleActions.changeRegisterInput({ name: name, value: value }));
};
dispatch(articleActions.changeRegisterInput({ name: name, value: value }));
이 부분이 반복됨을 알 수 있다.
원래는 title과 content가 서로 다르게 사용될 수도 있을 때를 고려하여 onChange 메소드를 각각 만든 것인데
지금은 크게 상관이 없으므로
다음과 같이 합쳐주기로 한다.
const onRegisterChange = (event) => {
const { name, value } = event.target;
dispatch(articleActions.changeRegisterInput({ name: name, value: value }));
};
그리고 RegisterPage의 컴포넌트 return 부분 메소드도
return (
<>
<RegisterOrEdit
titleValue={title}
contentValue={content}
handleRegisterChange={onRegisterChange} // 수정
handleSubmit={onSubmitArticle}
updateRequest={IsForUpdate}
/>
</>
);
이렇게 한 줄로 합쳐주고
property 이름이 변경됐기 때문에
RegisterOrEdit 으로 가서 바꿔줘야 한다.
<form onSubmit={props.handleSubmit}>
<br />
<div style={{ width: "80%", margin: "2rem auto" }}>
<label>Title: </label>
<Input
onChange={props.handleRegisterChange} // 변경
value={props.titleValue}
type="text"
name="title"
/>
<hr></hr>
<TextArea
rows="30"
onChange={props.handleRegisterChange} // 변경
value={props.contentValue}
name="content"
/>
</div>
<Button type="primary" onClick={props.handleSubmit}>
{props.updateRequest ? "수정" : "등록"}
</Button>
</form>
이렇게 같은 액션 함수와 같은 메소드를 쓰는 onChange 이벤트로 바뀌게 되었다.
다음은 redux-saga의 select를 이용한 refactoring 을 해보겠다.
+) 추가
개발자창에 오류가 보여서 추가로 수정한다.
ArticleDetail 컴포넌트에 tbody 태그를 추가하겠다. 그리고 colspan 도 colSpan으로 수정한다. 이거는 JSX 변환 문제 때문인 것 같다.
// ArticleDetail.js
<tbody> {/* 추가 */}
<tr>
<th>번호</th>
<td>{props.id}</td>
<th>조회수</th>
<td>{props.views}</td>
</tr>
<tr>
<th>날짜</th>
<td>{new Date(props.date).toLocaleString()}</td>
</tr>
<tr>
<th>제목</th>
<td colSpan="3">{props.title}</td> {/* 수정 */}
</tr>
<tr>
<th>내용</th>
<td colSpan="3">{props.content}</td> {/* 수정 */}
</tr>
</tbody> {/* 추가 */}
목차로 돌아가기: binaryjourney.tistory.com/pages/ReactCRUD-create-board-tutorial