기존에 작성했던 React query 코드를 Custom Hook으로 작성한 걸 기억하기 위해 이 글을 남긴다.
기존에 있던 코드는 컴포넌트 안에 React query 관련한 코드가 있어서 코드 분리의 필요성을 느끼게 하는 코드였다.
// TodoItem.tsx
const TodoItem = (props: { item: Todo }) => {
const { id, title, content, isDone } = props.item;
const queryClient = useQueryClient();
const updateMutate = useMutation({
mutationKey: ['todos'],
mutationFn: updateTodo,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ['todos'],
});
},
});
const isDoneHandler = (id: string): void => {
updateMutate.mutate({
...props.item,
isDone: !isDone,
});
};
const deleteMutate: UseMutationResult<Todo, Error, string, unknown> =
useMutation({
mutationKey: ['todos'],
mutationFn: removeTodo,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ['todos'],
});
},
});
const deleteTodoHandler = (id: string): void => {
deleteMutate.mutate(id);
};
return (
<Container $done={isDone}>
<TitleContainer>{title}</TitleContainer>
<ContentContainer>{content}</ContentContainer>
<ButtonContainer>
<DoneButton
onClick={() => {
isDoneHandler(id);
}}
>
{isDone ? '취소' : '완료'}
</DoneButton>
<DeleteButton
onClick={() => {
deleteTodoHandler(id);
}}
>
삭제
</DeleteButton>
</ButtonContainer>
</Container>
);
};
export default TodoItem;
// NewTodo.tsx
const NewTodo = () => {
const queryClient = useQueryClient();
const [newTodoTitle, titleHandler] = useInput<string>('');
const [newTodoContent, contentHandler] = useInput<string>('');
const addMutation: UseMutationResult<Todo, Error, Todo, unknown> =
useMutation({
mutationKey: ['todos'],
mutationFn: addTodo,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['todos'] });
},
});
const submitHandler = (e: FormEvent): void => {
e.preventDefault();
addMutation.mutate({
id: uuid(),
title: newTodoTitle,
content: newTodoContent,
isDone: false,
});
};
return (
<TodoForm onSubmit={submitHandler}>
<TodoTitleInput
onChange={titleHandler}
placeholder='제목'
value={newTodoTitle}
required
/>
<TodoContentInput
onChange={contentHandler}
placeholder='내용'
value={newTodoContent}
required
/>
<TodoButton>등록하기</TodoButton>
</TodoForm>
);
};
export default NewTodo;
위 두 코드를 커스텀 훅으로 빼서 따로 관리하면 어떨까? 라는 생각이 들었고, useTodoQuery라는 훅으로 따로 빼 만들었다.
// useTodoQuery.ts
import {
UseMutationResult,
useMutation,
useQuery,
useQueryClient,
} from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { addTodo, getTodoData, removeTodo, updateTodo } from '../apis/todoApi';
const useTodoQuery = () => {
const queryClient = useQueryClient();
const { data } = useQuery<Todo[], AxiosError>({
queryKey: ['todos'],
queryFn: getTodoData,
});
const addMutation: UseMutationResult<Todo, Error, Todo, unknown> =
useMutation({
mutationKey: ['todos'],
mutationFn: addTodo,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['todos'] });
},
});
const updateMutatation = useMutation({
mutationKey: ['todos'],
mutationFn: updateTodo,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ['todos'],
});
},
});
const deleteMutatation: UseMutationResult<Todo, Error, string, unknown> =
useMutation({
mutationKey: ['todos'],
mutationFn: removeTodo,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ['todos'],
});
},
});
return {
todos: data,
addTodo: addMutation,
updateTodo: updateMutatation,
removeTodo: deleteMutatation,
};
};
export default useTodoQuery;
// TodoItem.tsx
const TodoItem = (props: { item: Todo }) => {
const { id, title, content, isDone } = props.item;
const { updateTodo, removeTodo } = useTodoQuery();
const isDoneHandler = (id: string): void => {
updateTodo.mutate({
...props.item,
isDone: !isDone,
});
};
// NewTodo.tsx
const NewTodo = () => {
const [newTodoTitle, titleHandler, titleReset] = useInput<string>('');
const [newTodoContent, contentHandler, contentReset] = useInput<string>('');
const { addTodo } = useTodoQuery();
const submitHandler = (e: FormEvent): void => {
e.preventDefault();
addTodo.mutate({
id: uuid(),
title: newTodoTitle,
content: newTodoContent,
isDone: false,
});
titleReset();
contentReset();
};
기존 코드에 비해 코드가 훨씬 간결해지고 코드의 가독성이 올라갔다.
막연하게 Custom Hook을 작성하는것에 대한 두려움과 에러가 나면 어떡하지 라는 생각이 기존에는 존재했지만, 한번 작성하고 나니 왜 이렇게 좋은걸 지금까지 활용하지 못했나 하는 생각이 들었다.
그냥 함수를 따로 빼내어 필요한 기능만 모듈처럼 불러오는것과 비슷하다는 것을 깨달았다.
앞으로 Custom Hook을 더 적극적으로 활용해야겠다.
오....배워갑니다.....