-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(react-query): react query todo list 구현
리액트 쿼리를 이용해서 todolist 구현해봄 api router를 backend api로 사용 close #18
- Loading branch information
Showing
11 changed files
with
337 additions
and
177 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"singleQuote": true, | ||
"semi": true, | ||
"useTabs": false, | ||
"tabWidth": 2, | ||
"trailingComma": "all", | ||
"printWidth": 80, | ||
"parser": "typescript" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { useQueryClient } from 'react-query'; | ||
import { Todo } from '~/data/todo/todo.model'; | ||
import { useUpdateTodo, useDeleteTodo } from '~/data/todo/todo.hooks'; | ||
import { ChangeEvent, useState } from 'react'; | ||
|
||
interface TodoItemProps { | ||
todo: Todo; | ||
} | ||
|
||
function TodoItem({ todo }: TodoItemProps) { | ||
const queryClient = useQueryClient(); | ||
const [value, setValue] = useState(todo.text); | ||
|
||
const onChange = (e: ChangeEvent<HTMLInputElement>) => { | ||
setValue(e.target.value); | ||
}; | ||
|
||
const updateMutation = useUpdateTodo({ | ||
onError(error) { | ||
console.error(error); | ||
}, | ||
}); | ||
|
||
const deleteMutation = useDeleteTodo({ | ||
onSuccess() { | ||
queryClient.invalidateQueries('todos'); | ||
}, | ||
onError(error) { | ||
console.error(error); | ||
}, | ||
}); | ||
|
||
const onUpdate = (todo: Todo) => { | ||
updateMutation.mutate({ | ||
...todo, | ||
}); | ||
}; | ||
|
||
const onDelete = (id: number) => { | ||
deleteMutation.mutate({ | ||
id, | ||
}); | ||
}; | ||
|
||
return ( | ||
<div className="Todo__Item" key={todo.id}> | ||
<p> | ||
<input type="text" value={value} onChange={onChange} /> | ||
<button className="Update__Button" onClick={() => onUpdate(todo)}> | ||
Update | ||
</button> | ||
<button className="Delete__Button" onClick={() => onDelete(todo.id)}> | ||
X | ||
</button> | ||
</p> | ||
</div> | ||
); | ||
} | ||
|
||
export default TodoItem; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import axios from 'axios'; | ||
import { Todo } from './todo.model'; | ||
|
||
const ENDPOINT = 'http://localhost:3000/api'; | ||
|
||
export async function getTodos() { | ||
const { data } = await axios.get<Todo[]>(`${ENDPOINT}/todo`); | ||
return data; | ||
} | ||
|
||
export async function createTodo(requestBody: Todo) { | ||
return axios.post<unknown>(`${ENDPOINT}/todo`, requestBody); | ||
} | ||
|
||
export async function updateTodo(requestBody: Todo) { | ||
return await axios.put<unknown>(`${ENDPOINT}/todo`, requestBody); | ||
} | ||
|
||
export async function deleteTodo(data: { id: number }) { | ||
return await axios.delete<unknown>(`${ENDPOINT}/todo`, { | ||
data, | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { | ||
useQuery, | ||
UseQueryOptions, | ||
useMutation, | ||
UseMutationOptions, | ||
} from 'react-query'; | ||
import { AxiosResponse, AxiosError } from 'axios'; | ||
|
||
import { Todo } from './todo.model'; | ||
import { getTodos, createTodo, updateTodo, deleteTodo } from './todo.api'; | ||
|
||
export function useTodoList( | ||
/** | ||
* @see https://github.com/tannerlinsley/react-query/discussions/1195 | ||
* query variables가 필요할 때 | ||
*/ | ||
/** | ||
* @see https://github.com/tannerlinsley/react-query/discussions/1477 | ||
* TQueryFnData : Query 함수의 반환 데이터 | ||
* TError: Query 함수의 에러 반환값 | ||
* TData: Query 함수의 최종 데이터 | ||
*/ | ||
options: | ||
| UseQueryOptions<Todo[], AxiosError<unknown>, Todo[]> | ||
| undefined = {}, | ||
) { | ||
return useQuery<Todo[], AxiosError>('todos', getTodos, { | ||
retry: 2, | ||
...options, | ||
}); | ||
} | ||
|
||
export function useCreateTodo( | ||
/** | ||
* TData: 결과값 | ||
* TError | ||
* TVariables: mutation variables | ||
*/ | ||
options: UseMutationOptions< | ||
AxiosResponse<unknown>, | ||
AxiosError<unknown>, | ||
Todo | ||
>, | ||
) { | ||
return useMutation<AxiosResponse<unknown>, AxiosError, Todo>( | ||
'createTodo', | ||
createTodo, | ||
{ | ||
retry: false, | ||
...options, | ||
}, | ||
); | ||
} | ||
|
||
export function useUpdateTodo( | ||
options: UseMutationOptions< | ||
AxiosResponse<unknown>, | ||
AxiosError<unknown>, | ||
Todo | ||
>, | ||
) { | ||
return useMutation<AxiosResponse<unknown>, AxiosError, Todo>( | ||
'updateTodo', | ||
updateTodo, | ||
{ | ||
retry: false, | ||
...options, | ||
}, | ||
); | ||
} | ||
|
||
export function useDeleteTodo( | ||
options: UseMutationOptions< | ||
AxiosResponse<unknown>, | ||
AxiosError<unknown>, | ||
{ id: number } | ||
>, | ||
) { | ||
return useMutation<AxiosResponse<unknown>, AxiosError, { id: number }>( | ||
'deleteTodo', | ||
deleteTodo, | ||
{ | ||
retry: false, | ||
...options, | ||
}, | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export type Todo = { | ||
id: number; | ||
text: string; | ||
isDone: boolean; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import type { NextApiRequest, NextApiResponse } from 'next'; | ||
|
||
type Todo = { | ||
id: number; | ||
text: string; | ||
isDone: boolean; | ||
}; | ||
|
||
let initialState: Todo[] = [ | ||
{ id: 0, text: 'React-Query 스터디', isDone: false }, | ||
{ id: 1, text: 'jest 스터디', isDone: false }, | ||
{ id: 2, text: 'msw 스터디', isDone: false }, | ||
]; | ||
|
||
export default (req: NextApiRequest, res: NextApiResponse) => { | ||
const { method, body } = req; | ||
|
||
switch (req.method) { | ||
case 'GET': | ||
res.status(200).json(initialState); | ||
break; | ||
case 'POST': | ||
initialState.push(body); | ||
res.status(200).json({ message: 'create todo success' }); | ||
break; | ||
case 'PUT': | ||
initialState = initialState.map((todo) => { | ||
if (todo.id === body.id) { | ||
return { | ||
...todo, | ||
...body, | ||
}; | ||
} else { | ||
return todo; | ||
} | ||
}); | ||
res.status(200).json({ message: 'update todo success' }); | ||
break; | ||
case 'DELETE': | ||
initialState = initialState.filter((todo) => todo.id !== body.id); | ||
res.status(200).json({ message: 'delete todo success' }); | ||
break; | ||
default: | ||
res.setHeader('Allow', ['GET', 'POST', 'PUT', 'DELETE']); | ||
res.status(405).end(`Method ${method} Not Allowed`); | ||
} | ||
}; |
Oops, something went wrong.