前一篇我們完成了基本的 getAll request,並於畫面簡單呈現表單內容,那麼今天我們就接續完成他應該有的修改與刪除能吧!
讓我們先回到 todoApi 裡面新增修改狀態的 request,如下:
// src/features/api/todoApi.ts
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"
// 做一份與 server 相同的 Todo 格式提供 component 使用
export interface Todo {
id: number;
text: string;
active: boolean;
done: boolean;
}
// 這裡 createApi 會自動將 endpoints 的 name 去組成相對應的 query
// 以 getAll 為例,使用時會組成 useGetAllQuery 的 function
export const todoApi = createApi({
reducerPath: 'todoApi',
baseQuery: fetchBaseQuery({ baseUrl: 'http://localhost:8080/'}),
tagTypes: ['Todos'],
endpoints: (builder) => ({
// 取全部資料
getAll: builder.query<Todo[], void>({
// 這裡的 query 代表接續 base 要傳入的值,providesTags 就是記錄一下對應的 tagType 詳細 https://redux-toolkit.js.org/rtk-query/usage/automated-refetchin
query: () => `todos`,
providesTags: [{type: 'Todos', id: 'LIST'}]
}),
// 修改 todo
// 這裡採用 mutation 意思是會拿取先前的 todo 然後回傳一個新的 todo 這段的概念與 React query 是相同的。
updateTodo: builder.mutation<Todo, Todo>({
// 這裡就是帶入參數的做法
query(todo) {
return {
url: `todos/${todo.id}`,
method: 'PUT',
body: todo,
};
},
// invalidatesTag 會自動去更新相對的 tagType 詳細一樣 https://redux-toolkit.js.org/rtk-query/usage/automated-refetchin
invalidatesTags: [{type: 'Todos', id: 'LIST'}]
})
})
})
接著,我們來修改畫面,讓我們的 checkbox 能夠正常運作:
// src/components/TodosList.tsx
import { useCallback } from "react";
import { Todo, todoApi } from "../features/api/todoApi"
const TodosList = () => {
// 使用剛才的 getAll query
const { data } = todoApi.useGetAllQuery();
// 這裡使用剛才的 updateTodo
const [ updateTodo ] = todoApi.useUpdateTodoMutation();
// 這裡的話就是一般 react 的常規操作
const onToggle = useCallback((todo: Todo) => {
updateTodo({...todo, done: !todo.done})
}, [updateTodo])
return (
<div className="card" style={{margin: '1rem 0'}}>
{data?.map((todo) => (
<div className="f-b-c" key={todo.id}>
<div>
<input type="checkbox" checked={todo.done} onChange={() => onToggle(todo)}/>
<span>{todo.text}</span>
</div>
<button>delete</button>
</div>
))}
</div>
)
}
export default TodosList
此時可以測試看看,於 devtool 裏面的 network 面板應該可以確實看到有將 request 打出去,那麼我們接著處理刪除的部分,我們回到 todoApi 裡面心怎 delete request,如下:
// src/features/api/todoApi.ts
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"
// 做一份與 server 相同的 Todo 格式提供 component 使用
export interface Todo {
id: number;
text: string;
active: boolean;
done: boolean;
}
// 這裡 createApi 會自動將 endpoints 的 name 去組成相對應的 query
// 以 getAll 為例,使用時會組成 useGetAllQuery 的 function
export const todoApi = createApi({
reducerPath: 'todoApi',
baseQuery: fetchBaseQuery({ baseUrl: 'http://localhost:8080/'}),
tagTypes: ['Todos'],
endpoints: (builder) => ({
// 取全部資料
getAll: builder.query<Todo[], void>({
// 這裡的 query 代表接續 base 要傳入的值,providesTags 就是記錄一下對應的 tagType 詳細 https://redux-toolkit.js.org/rtk-query/usage/automated-refetchin
query: () => `todos`,
providesTags: [{type: 'Todos', id: 'LIST'}]
}),
// 修改 todo
// 這裡採用 mutation 意思是會拿取先前的 todo 然後回傳一個新的 todo 這段的概念與 React query 是相同的。
updateTodo: builder.mutation<Todo, Todo>({
// 這裡就是帶入參數的做法
query(todo) {
return {
url: `todos/${todo.id}`,
method: 'PUT',
body: todo,
};
},
// invalidatesTag 會自動去更新相對的 tagType 詳細一樣 https://redux-toolkit.js.org/rtk-query/usage/automated-refetchin
invalidatesTags: [{type: 'Todos', id: 'LIST'}]
}),
// 刪除功能
deleteTodo: builder.mutation<Todo, Todo>({
query(todo) {
return {
url: `todos/${todo.id}`,
method: 'DELETE',
body: todo,
};
},
invalidatesTags: [{type: 'Todos', id: 'LIST'}]
})
})
})
差不多類似的情形,我們再處理一下畫面,讓 delete button 能有反應:
// src/components/TodosList.tsx
import { useCallback } from "react";
import { Todo, todoApi } from "../features/api/todoApi"
const TodosList = () => {
// 使用剛才的 getAll query
const { data } = todoApi.useGetAllQuery();
// 這裡使用剛才的 updateTodo
const [ updateTodo ] = todoApi.useUpdateTodoMutation();
// 這裡使用剛才的 deleteTodo
const [ deleteTodo ] = todoApi.useDeleteTodoMutation();
// 這裡的話就是一般 react 的常規操作
const onToggle = useCallback((todo: Todo) => {
updateTodo({...todo, done: !todo.done})
}, [updateTodo])
// 與上述相同再操作一次
const onDelete = useCallback((todo: Todo) => {
deleteTodo(todo)
}, [deleteTodo])
return (
<div className="card" style={{margin: '1rem 0'}}>
{data?.map((todo) => (
<div className="f-b-c" key={todo.id}>
<div>
<input type="checkbox" checked={todo.done} onChange={() => onToggle(todo)}/>
<span>{todo.text}</span>
</div>
<button onClick={() => onDelete(todo)}>delete</button>
</div>
))}
</div>
)
}
export default TodosList
此時一樣可以測試看看刪除的按鈕使否有正常運行,如果有的話,同樣於 network 面板能看到我們送出的 delete request。
那麼以上已經完成了表單內的基本功能,下一篇我們來做新增的部分。