iT邦幫忙

2022 iThome 鐵人賽

DAY 29
1
自我挑戰組

30天深入淺出Redux系列 第 29

Redux 深入淺出 - [ Day 29 ] React RTK Query Typescript todo list (更新 & 刪除)

  • 分享至 

  • xImage
  •  

前一篇我們完成了基本的 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。

那麼以上已經完成了表單內的基本功能,下一篇我們來做新增的部分。


上一篇
Redux 深入淺出 - [ Day 28 ] React RTK Query Typescript 實作
下一篇
Redux 深入淺出 - [ Day 30 ] React RTK Query Typescript todo list - 新增功能 & 結語
系列文
30天深入淺出Redux31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言