Skip to content

Commit e14052a

Browse files
committed
API Client & HTTP Service
1 parent 3353301 commit e14052a

File tree

6 files changed

+85
-34
lines changed

6 files changed

+85
-34
lines changed

src/react-query/TodoForm.tsx

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,12 @@
1-
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
1+
22
import { useRef } from 'react';
3-
import { Todo } from './hooks/useTodos';
4-
import axios from 'axios';
3+
import useAddTodo from './hooks/useAddTodo';
54

65
const TodoForm = () => {
7-
const queryClient = useQueryClient();
8-
const addTodo = useMutation<Todo, Error, Todo>({
9-
mutationFn: (todo: Todo) =>
10-
axios
11-
.post<Todo>('https://jsonplaceholder.typicode.com/todos', todo)
12-
.then(res => res.data),
13-
onSuccess: (savedTodo, newTodo) => {
14-
// queryClient.invalidateQueries({
15-
// queryKey: ['todos']
16-
// })
17-
queryClient.setQueryData<Todo[]>(['todos'], todos => [savedTodo, ...(todos || [])]);
18-
19-
if(ref.current) ref.current.value = '';
20-
}
21-
});
226
const ref = useRef<HTMLInputElement>(null);
7+
const addTodo = useAddTodo(() => {
8+
if(ref.current) ref.current.value = '';
9+
})
2310
return (
2411
<>
2512
{ addTodo.error && <div className="alert alert-danger">
@@ -40,8 +27,8 @@ const TodoForm = () => {
4027
<input ref={ref} type="text" className="form-control" />
4128
</div>
4229
<div className="col">
43-
<button className="btn btn-primary" disabled={addTodo.isLoading}>
44-
{addTodo.isLoading ? 'Adding...' : 'Add'}
30+
<button className="btn btn-primary">
31+
Add
4532
</button>
4633
</div>
4734
</form>

src/react-query/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const CACHE_KEY_TODOS = ['todos'];
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { useMutation, useQueryClient } from "@tanstack/react-query";
2+
import { CACHE_KEY_TODOS } from "../constants";
3+
import todoService, { Todo } from "../services/todoService";
4+
5+
6+
interface AddTodoContext {
7+
previousTodos: Todo[]
8+
}
9+
10+
const useAddTodo = (onAdd: () => void) => {
11+
const queryClient = useQueryClient();
12+
return useMutation<Todo, Error, Todo, AddTodoContext>({
13+
mutationFn: todoService.post,
14+
15+
onMutate: (newTodo: Todo) => {
16+
const previousTodos = queryClient.getQueryData<Todo[]>(CACHE_KEY_TODOS) || [];
17+
18+
queryClient.setQueryData<Todo[]>(CACHE_KEY_TODOS, (todos = []) => [newTodo, ...todos]);
19+
20+
onAdd();
21+
22+
return { previousTodos };
23+
},
24+
25+
onSuccess: (savedTodo, newTodo) => {
26+
queryClient.setQueryData<Todo[]>(CACHE_KEY_TODOS, todos => todos?.map(todo => todo === newTodo ? savedTodo : todo));
27+
},
28+
29+
onError: (error, newTodo, context) => {
30+
if (!context) return;
31+
32+
queryClient.setQueryData<Todo[]>(CACHE_KEY_TODOS, context.previousTodos);
33+
}
34+
});
35+
}
36+
37+
export default useAddTodo;

src/react-query/hooks/useTodos.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,12 @@
11
import { useQuery } from "@tanstack/react-query";
2-
import axios from "axios";
2+
import { CACHE_KEY_TODOS } from "../constants";
3+
import todoService, { Todo } from "../services/todoService";
34

4-
export interface Todo {
5-
id: number;
6-
title: string;
7-
userId: number;
8-
completed: boolean;
9-
}
105

116
const useTodos= () => {
12-
const fetchTodos = () =>
13-
axios
14-
.get<Todo[]>("https://jsonplaceholder.typicode.com/todos")
15-
.then((res) => res.data);
16-
177
return useQuery<Todo[], Error>({
18-
queryKey: ["todos"],
19-
queryFn: fetchTodos,
8+
queryKey: CACHE_KEY_TODOS,
9+
queryFn: todoService.getAll,
2010
staleTime: 10 * 1000
2111
});
2212
};
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import axios from "axios";
2+
3+
const axiosInstance = axios.create({
4+
baseURL: "https://jsonplaceholder.typicode.com",
5+
});
6+
7+
class APIClient<T> {
8+
endpoint: string;
9+
10+
constructor(endpoint: string) {
11+
this.endpoint = endpoint;
12+
}
13+
14+
getAll = () => {
15+
return axiosInstance
16+
.get<T[]>(this.endpoint)
17+
.then((res) => res.data);
18+
}
19+
post = (data: T) => {
20+
return axiosInstance
21+
.post<T>(this.endpoint, data)
22+
.then(res => res.data);
23+
}
24+
}
25+
26+
export default APIClient;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import APIClient from "./apiClient";
2+
3+
export interface Todo {
4+
id: number;
5+
title: string;
6+
userId: number;
7+
completed: boolean;
8+
}
9+
10+
export default new APIClient<Todo>('/todos');

0 commit comments

Comments
 (0)