카드 리스트를 불러오기 위해 Read 먼저 구현해주었다.
api/studyTodo.js
import axios from "axios";
// 조회
const getStudyList = async () => {
const response = await axios.get(`${process.env.REACT_APP_SERVER_URL}/study`);
console.log("res", response);
return response.data;
};
export { getStudyList };
CardList에서 Card로 넘겨주고, react-query로 오류 처리를 해줬다.
카드의 완료 상태에 따라 색이 다르도록 설정해줬다.
components/CardList.jsx
function CardList() {
const { isLoading, isError, data } = useQuery("study", getStudyList);
if (isLoading) {
return <h1>로딩중</h1>;
}
if (isError) {
return <h1>오류가 발생하였습니다....!!!</h1>;
}
return (
<div>
<CardListBox>
{data.map((item) => {
return <Card key={item.id} study={item} />;
})}
</CardListBox>
</div>
);
}
components/Card.jsx
function Card({ study }) {
return (
<CardBox done={study.isDone}>
<CardNav>
<div>작성자 {study.writer}</div>
<MdDeleteForever IoIosClose size="25"></MdDeleteForever>
</CardNav>
<Cardcontext>{study.title}</Cardcontext>
<Cardfooter>
<div>
<Button color="#ffffff">{study.isDone ? "완료" : "미완료"}</Button>
</div>
</Cardfooter>
</CardBox>
);
}
export default Card;
const CardBox = styled.div`
background-color: ${(props) => (props.done ? "#9dc3c1" : "#6E7783")};
width: 18rem;
border-radius: 4px;
margin: 1rem;
flex-direction: column;
&:hover {
cursor: pointer;
}
`;
post에서 input을 사용하는데 중복 코드를 줄이기 위해 custom hook을 만들었다.
useInput.js
import { useState } from "react";
const useInput = () => {
// state
const [value, setValue] = useState("");
// handler
const handler = (e) => {
setValue(e.target.value);
};
return [value, handler];
};
export default useInput;
post.jsx
const [title, onChangeTitleHandler] = useInput();
const [writer, onChangeWriterHandler] = useInput();
const [content, onChangeContentHandler] = useInput();
.
.
<div>
<label>제목</label>
<input value={title} onChange={onChangeTitleHandler} />
</div>
<div>
<label>작성자</label>
<input value={writer} onChange={onChangeWriterHandler} />
</div>
<div>
<label>내용</label>
<textarea value={content} onChange={onChangeContentHandler} />
</div>
const PostButton = styled.div`
position: fixed;
bottom: 40px;
right: 30px;
`;
다음과 같이 위치를 고정하면 스크롤을 내려도 항상 같은 위치에 나타난다.
react query를 이용해서 input을 customhook을 이용하고 해당 값이 저장되도록 코딩하였다.
const queryClient = useQueryClient();
const mutation = useMutation(addStudyList, {
onSuccess: () => {
queryClient.invalidateQueries("study");
console.log("Post 성공");
},
});
const handleSubmitButtonClick = (event) => {
event.preventDefault();
// 유효성 검사
// 1. 제목, 내용 모두 입력되어야 한다.
if (!title || !writer) {
return alert("제목 또는 내용이 입력되지 않았습니다!");
}
const newStudyTodo = {
title,
contents,
writer,
isDone: false,
};
mutation.mutate(newStudyTodo);
setTitle("");
setWriter("");
setContent("");
alert("저장되었습니다!");
navigate("/");
};
제목이나 내용이 빈값이면 저장되지 않고 경고창을 나타나게 만들었고, mutate를 통해 addStudyList를 동작하여 study 쿼리에 newStudyTodo가 post되고 기존 쿼리를 queryClient.invalidateQueries("study");
로 무효화 하여 렌더링되게 만들어준다.
url을 id를 통해 자세히 불러올 수 있어서 api/studyTodo.js에 상세 페이지를 조회하는 코드를 작성했다.
// 상세 페이지 조회
const getStudy = async (id) => {
const response = await axios.get(
`${process.env.REACT_APP_SERVER_URL}/study/${id}`
);
console.log("detail res", response);
return response.data;
};
불러올 때 해당 코드를 작성했는데
const { isLoading, isError, data } = useQuery(`${param.id}`,
getStudy(param.id)
);
계속 실행을 해서 어디가 문제인가 했는데, 화살표함수를 사용하지 않아서 계속 무한 렌더링이 발생하였다.
const { isLoading, isError, data } = useQuery(`${param.id}`, () =>
getStudy(param.id)
);
이렇게 고쳐주면 값이 한번만 잘 받아진다!
서버와 통신을 하면서 값이 바뀌어 렌더링이 되는데, 어떻게 줄일 수 있는 방법이 있을지 고민하다가 튜터님은 통신을 하는 부분에서 발생하는 것은 어쩔 수 없이 가져간다고 하신다.
이번에 query를 직접 적용해보면서 진행하였는데, query와 redux가 어떤점이 다를까 고민해보았다. query는 통신이 있을 때 사용하고 redux는 통신이 필요없는 상태를 관리할 때 사용하는 것 같다.