useQuery는 get할때 사용하는 메서드였다.
create했을때 리스트가 업데이트되지않아서 당황했음.
refresh를 삭제해서 그런거겠지만... 리액트쿼리에 이부분을 위한 메서드가 있을거라고 생각했다.
찾아보니 useMutation이라는 것을 사용해서 CUD를 하고, 여기서 refetch해주는 게 따로 있다고 함.
리액트 쿼리 mutation
https://react-query-v3.tanstack.com/guides/mutations
import Input from "../../../common/Input/Input";
import Button from "../../../common/button/Button";
import { useState } from "react";
import { createTodo } from "../../../api/TodoApi";
import { AiFillCloseSquare } from "react-icons/ai";
import * as TodoCreateStyle from "./TodoCreateStyle";
import Textarea from "../../../common/textaea/Textarea";
import IconButton from "../../../common/iconButton/IconButton";
const TodoCreate = ({ refresher, handleAddMode }) => {
const [todoContent, setTodoContent] = useState({
title: "",
content: "",
});
const { title, content } = todoContent;
const handleTodoContent = (e) => {
const { value, name } = e.target;
setTodoContent({
...todoContent,
[name]: value,
});
};
const reset = (e) => {
setTodoContent({
title: "",
content: "",
});
};
function handleCreateTodo(e) {
e.preventDefault();
const payload = todoContent;
createTodo("/todos", payload);
reset(e);
refresher();
}
return (
<>
<TodoCreateStyle.CreateForm onSubmit={handleCreateTodo}>
<TodoCreateStyle.CreateTitle>
<h3>Todo 추가하기</h3>
<IconButton onClick={handleAddMode} type="submit">
<AiFillCloseSquare />
</IconButton>
</TodoCreateStyle.CreateTitle>
<Input
name="title"
id="todo Title"
type="text"
placeholder="오늘 할 일을 적어보세요."
border="border"
value={title}
onChange={handleTodoContent}
/>
<Textarea name="content" value={content} onChange={handleTodoContent} />
<Button styles="default" type="submit">
Add Todo
</Button>
</TodoCreateStyle.CreateForm>
</>
);
};
export default TodoCreate;
import Input from "../../../common/Input/Input";
import Button from "../../../common/button/Button";
import { useState } from "react";
import { createTodo, updateTodo } from "../../../api/TodoApi";
import { AiFillCloseSquare } from "react-icons/ai";
import * as TodoCreateStyle from "./TodoCreateStyle";
import Textarea from "../../../common/textaea/Textarea";
import IconButton from "../../../common/iconButton/IconButton";
import { useMutation, useQueryClient } from "react-query";
const TodoCreate = ({ refresher, handleAddMode }) => {
const [todoContent, setTodoContent] = useState({
title: "",
content: "",
});
const { title, content } = todoContent;
const handleTodoContent = (e) => {
const { value, name } = e.target;
setTodoContent({
...todoContent,
[name]: value,
});
};
const reset = (e) => {
setTodoContent({
title: "",
content: "",
});
};
const queryClient = useQueryClient();
const { mutate, isLoading } = useMutation(createTodo, {
//Updating the cached data using the response data
onSuccess: (newTodo) => {
queryClient.invalidateQueries(["todo"], newTodo);
},
});
function handleCreateTodo(e) {
e.preventDefault();
mutate(todoContent);
reset(e);
}
if (isLoading) {
return <div>is Loading...</div>;
}
return (
<>
<TodoCreateStyle.CreateForm onSubmit={handleCreateTodo}>
<TodoCreateStyle.CreateTitle>
<h3>Todo 추가하기</h3>
<IconButton onClick={handleAddMode} type="submit">
<AiFillCloseSquare />
</IconButton>
</TodoCreateStyle.CreateTitle>
<Input
name="title"
id="todo Title"
type="text"
placeholder="오늘 할 일을 적어보세요."
border="border"
value={title}
onChange={handleTodoContent}
/>
<Textarea name="content" value={content} onChange={handleTodoContent} />
<Button styles="default" type="submit">
Add Todo
</Button>
</TodoCreateStyle.CreateForm>
</>
);
};
export default TodoCreate;
queryClient.setQueryData('todos', old => [...old, newTodo])
이런식으로 작성했더니 바로 업데이트가 안된다.
위에서처럼queryClient.invalidateQueries(["todo"], newTodo);
이렇게 써야 업뎃이 바로 됨. 유니크 키로 맵핑된
get함수를 실행
하게 된다.
setQueryData는 get함수의 파라미터를변경
해야할때 사용한다고 한다.
import { useEffect, useState } from "react";
import { deleteTodo, getSpecificTodo } from "../../../api/TodoApi";
import EditMode from "../editMode/EditMode";
import { useNavigate } from "react-router-dom";
interface TodoType {
title?: string,
content?: string,
createdAt?: string,
}
const TodoDetail = ({curParams, refresh, refresher}) => {
const [edit, setEdit] = useState(false);
const [detailTodo, setDetailTodo] = useState({});
const {title, content, createdAt}: TodoType = detailTodo;
const navigate = useNavigate();
useEffect(() => {
const fetchData = async () => {
const res = await getSpecificTodo('/todos/', curParams);
setDetailTodo(res.data);
}
fetchData();
}, [curParams, refresh]);
const handleDelete = (e) => {
e.preventDefault();
deleteTodo('/todos/', curParams)
navigate('/');
refresher();
};
return (
<>
<TodoDetailStyle.TodoDetailBox>
<TodoDetailStyle.TodoTitle>
<BiCircle />
<h3>{title}</h3>
<TodoDetailStyle.EditBox>
<IconButton onClick={handleDelete} type="button">
<BsTrashFill />
</IconButton>
<IconButton onClick={handleUpdateMode} type="button">
<FaEdit />
</IconButton>
</TodoDetailStyle.EditBox>
</TodoDetailStyle.TodoTitle>
<TodoDetailStyle.TodoContent>
<span>Date: {createdAt}</span>
<p>{content}</p>
</TodoDetailStyle.TodoContent>
</TodoDetailStyle.TodoDetailBox>
</>
)
}
export default TodoDetail;
import { deleteTodo } from "../../../api/TodoApi";
import { useNavigate } from "react-router-dom";
import { useMutation, useQueryClient } from "react-query";
const TodoDetail = ({ curParams }) => {
const navigate = useNavigate();
const queryClient = useQueryClient();
const { mutate } = useMutation(deleteTodo, {
onSuccess: () => {
queryClient.invalidateQueries(["todo"]);
},
});
const handleDelete = (e) => {
e.preventDefault();
mutate(curParams);
navigate("/");
};
return (
<>
<TodoDetailStyle.TodoDetailBox>
<TodoDetailStyle.TodoTitle>
<BiCircle />
<h3>{title}</h3>
<TodoDetailStyle.EditBox>
<IconButton onClick={handleDelete} type="button">
<BsTrashFill />
</IconButton>
<IconButton onClick={handleUpdateMode} type="button">
<FaEdit />
</IconButton>
</TodoDetailStyle.EditBox>
</TodoDetailStyle.TodoTitle>
<TodoDetailStyle.TodoContent>
<span>Date: {createdAt}</span>
<p>{content}</p>
</TodoDetailStyle.TodoContent>
</TodoDetailStyle.TodoDetailBox>
</>
};
export default TodoDetail;
리액트 쿼리를 사용하니 다른 코드에 대한 의존도가 낮아진 것 같다.
그놈의 리프레셔도 안써도 되구...
import { useEffect, useState } from "react";
import { deleteTodo, getSpecificTodo } from "../../../api/TodoApi";
import EditMode from "../editMode/EditMode";
import { useNavigate } from "react-router-dom";
const TodoDetail = ({ curParams, refresh, refresher }) => {
const [edit, setEdit] = useState(false);
const [detailTodo, setDetailTodo] = useState({});
const { title, content, createdAt }: TodoType = detailTodo;
const navigate = useNavigate();
useEffect(() => {
const fetchData = async () => {
const res = await getSpecificTodo(curParams);
setDetailTodo(res.data);
};
fetchData();
}, [curParams, refresh]);
const handleUpdateMode = () => {
setEdit(!edit);
};
return (
<>
<TodoDetailStyle.TodoDetailBox>
{edit ? (
<EditMode
curParams={curParams}
curTitle={title}
curContent={content}
refresher={refresher}
handleUpdateMode={handleUpdateMode}
/>
) : (
<>
<TodoDetailStyle.TodoTitle>
<BiCircle />
<h3>{title}</h3>
<TodoDetailStyle.EditBox>
<IconButton onClick={handleDelete} type="button">
<BsTrashFill />
</IconButton>
<IconButton onClick={handleUpdateMode} type="button">
<FaEdit />
</IconButton>
</TodoDetailStyle.EditBox>
</TodoDetailStyle.TodoTitle>
<TodoDetailStyle.TodoContent>
<span>Date: {createdAt}</span>
<p>{content}</p>
</TodoDetailStyle.TodoContent>
</>
)}
</TodoDetailStyle.TodoDetailBox>
</>
);
};
export default TodoDetail;
앞서 list를 적용한 것처럼 해봤는데 값이 undefined가 나온다. 뭔가 잘못된듯;
Cannot destructure property 'title' of 'data' as it is undefined.
계속 위와 같은 에러가 났었는데 다시 살펴보니 로딩전에 구조분해할당을 해서 그런거였당... 헷
useSearchParams를 사용해서 url 파라미터를 얻을 수 있다던데
일단 useParams를 쓰고잇음.
import { useState } from "react";
import { deleteTodo, getSpecificTodo } from "../../../api/TodoApi";
import EditMode from "../editMode/EditMode";
import { useNavigate } from "react-router-dom";
import { useMutation, useQuery, useQueryClient } from "react-query";
const TodoDetail = ({ curParams }) => {
const [edit, setEdit] = useState(false);
const navigate = useNavigate();
const fetchDetail = async ({ queryKey }) => {
const [_, id] = queryKey;
const res = await getSpecificTodo(id);
return res.data;
};
const { data, isLoading, isError } = useQuery(
["detailTodo", curParams],
fetchDetail
);
if (isLoading) {
return <div>is Loading...</div>;
}
if (isError) {
return <div>is Error...</div>;
}
const { title, content, createdAt }: TodoType = data;
//이부분이 문제였음!!!
return (
<>
<TodoDetailStyle.TodoDetailBox>
<TodoDetailStyle.TodoTitle>
<BiCircle />
<h3>{title}</h3>
<TodoDetailStyle.EditBox>
<IconButton onClick={handleDelete} type="button">
<BsTrashFill />
</IconButton>
<IconButton onClick={handleUpdateMode} type="button">
<FaEdit />
</IconButton>
</TodoDetailStyle.EditBox>
</TodoDetailStyle.TodoTitle>
<TodoDetailStyle.TodoContent>
<span>Date: {createdAt}</span>
<p>{content}</p>
</TodoDetailStyle.TodoContent>
</TodoDetailStyle.TodoDetailBox>
</>
);
};
export default TodoDetail;
import { useState } from "react";
import { updateTodo } from "../../../api/TodoApi";
import Input from "../../../common/Input/Input";
import Button from "../../../common/button/Button";
import * as EditModeStyle from './EditModeStyle';
import Textarea from "../../../common/textaea/Textarea";
const EditMode = ({curParams, refresher, curTitle, curContent, handleUpdateMode}) => {
const [updateTodoData, setUpdateTodoData] = useState({
title: '',
content: ''
});
const {title, content} = updateTodoData;
const handleUpdateTodo = (e) => {
const { value, name } = e.target;
setUpdateTodoData({
...updateTodoData,
[name]: value
});
}
const submitUpdateTodo = (e) => {
e.preventDefault();
const payload = {
title: title ? title : curTitle,
content: content ? content : curContent
}
updateTodo(curParams, payload);
refresher();
handleUpdateMode();
}
return (
<>
<EditModeStyle.Form onSubmit={submitUpdateTodo}>
<EditModeStyle.EditTitle>
<h3>Todo 수정하기</h3>
</EditModeStyle.EditTitle>
<div>
<EditModeStyle.TodoTitle>
<Input name="title"
id="edit Title"
value={title ? title : curTitle}
onChange={handleUpdateTodo}
type="text"
/>
</EditModeStyle.TodoTitle>
<Textarea name="content"
value={content ? content : curContent}
onChange={handleUpdateTodo}/>
</div>
<EditModeStyle.ButtonBox>
<Button styles="cancel" onClick={handleUpdateMode} type="button">취소하기</Button>
<Button styles="default" type="submit">수정하기</Button>
</EditModeStyle.ButtonBox>
</EditModeStyle.Form>
</>
)
}
export default EditMode;
funtcion is not assignable to parameter of type 'MutationKey'
useMutation은 한개의 인자만 받는다. 두개라서 계속 에러가 났음..
아 업데이트 어렵당....ㅠㅠ
아 콘솔로 찍어보고 알았다!!
{curParams, payload} 이렇게 api에 전달하는데
구조분해할당은 {todoId, parameter} 이렇게 하니까 undefined가 나와서 계속 에러가 났던것....
import { useState } from "react";
import { updateTodo } from "../../../api/TodoApi";
import { useMutation, useQueryClient } from "@tanstack/react-query";
const EditMode = ({ curParams, curTitle, curContent, handleUpdateMode }) => {
const [updateTodoData, setUpdateTodoData] = useState({
title: "",
content: "",
});
const { title, content } = updateTodoData;
const handleUpdateTodo = (e) => {
const { value, name } = e.target;
setUpdateTodoData({
...updateTodoData,
[name]: value,
});
};
const queryClient = useQueryClient();
const modifyMutation = useMutation({
mutationFn: updateTodo,
onSuccess: (modifiedData) => {
queryClient.setQueryData(["detailTodo", { id: curParams }], modifiedData);
queryClient.invalidateQueries(["detailTodo"]);
},
});
const submitUpdateTodo = (e) => {
e.preventDefault();
const todoId = curParams;
const parameter = {
title: title ? title : curTitle,
content: content ? content : curContent,
};
modifyMutation.mutate({ todoId, parameter });
handleUpdateMode();
};
return (
<>
<EditModeStyle.Form onSubmit={submitUpdateTodo}>
<EditModeStyle.EditTitle>
<h3>Todo 수정하기</h3>
</EditModeStyle.EditTitle>
<div>
<EditModeStyle.TodoTitle>
<Input
name="title"
id="edit Title"
value={title ? title : curTitle}
onChange={handleUpdateTodo}
type="text"
/>
</EditModeStyle.TodoTitle>
<Textarea
name="content"
value={content ? content : curContent}
onChange={handleUpdateTodo}
/>
</div>
<EditModeStyle.ButtonBox>
<Button styles="cancel" onClick={handleUpdateMode} type="button">
취소하기
</Button>
<Button styles="default" type="submit">
수정하기
</Button>
</EditModeStyle.ButtonBox>
</EditModeStyle.Form>
</>
);
};
export default EditMode;