This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

📢 Hello everyone, I am Xiao Cheng, a sophomore front-end enthusiast

📢 this series of articles is a learning summary of the jIRA task management system in action

📢 Thank you very much for reading

📢 May you be true to yourself and love life

In the last article, we realized the route jump and realized the corresponding project jump to the kanban page showing the corresponding content. In this process, we wrote useDocumentTitle and useDebounce as custom hooks. Next we will deal with the kanban part of the presentation

💡 Knowledge points look first

  • encapsulationKanbanColumnTo lay out the page
  • Write a large number ofcustom hookTo process kanban data
  • rightuseQueryHave further understanding
  • usingfilterAchieve data uniformity

A custom hook for handling kanban data

Here we need to solve the following problem of obtaining kanban data so that we can better drive the view

We write these hooks separately in a kanban.ts folder in util. Hooks in this folder are hooks with high reusability and little relation to the page

1. useKanbans

The method of obtaining data here is the same as the previous method of obtaining project data. We use useQuery to cache kanpan data. Here, we need to receive a Param as a parameter and pass the current projectId. We need to request the following again

export const useKanbans = (param? : Partial
       ) = > {
    UseHttp is used to encapsulate requests
    const client = useHttp()
    // Map a cached data named Kanbans, resend the request when param changes, write to the cache
    return useQuery<Kanban[]>(['kanbans', param], () = > client('kanbans', { data: param }))
}
Copy the code

Now that we’ve wrapped Usekanbans, we’re able to get kanban data in our project, so let’s wrap a Custom Hook to get projectId to implement usekanbans

2. useProjectIdInUrl

We’ll write this code in util under kanban because it’s directly related to the project

First of all, in our previous routing processing, we mapped our projectId to the url, and we can obtain the projectId requested by the current page by resolving this url address

Here we use the React-router hook to get the pathname, which is in the format /projects/1/kanban

So we use the regular expression to get the number, which is our proejctId, and finally return the numeric type of the ID

export const useProjectIdInUrl = () = > {
    const { pathname } = useLocation()
    // Returns an array
    const id = pathname.match(/projects\/(\d+)/)? .1]
    return Number(id)
}
Copy the code

3. useProjectInUrl

With our projectId, we can use it to obtain our project data, so that we can obtain the name of our project and display it on the page

// Get project information by id
export const useProjectInUrl = () = > useProject(useProjectIdInUrl())
Copy the code

use

const { data: currentProject } = useProjectInUrl() <h1>{currentProject? .name} kanban < / h1 >Copy the code

Now that we have kanban data and project information, we need to get the corresponding task information

4. useKanbanSearchParams

In order to avoid getting kanban data from the entire project, we need to pass the ID to key-value to useKanbans to get the data

export const useKanbanSearchParams = () = > ({ projectId: useProjectIdInUrl() })
Copy the code

5. useTasks

Then we need to get the task data, the task data for our project

As with obtaining kanban data, we need to use useQuery for processing

export const useTasks = (param? : Partial
       ) = > {
    const client = useHttp()
    // The search box request is triggered here
    return useQuery<Task[]>(['tasks', param], () = > client('tasks', { data: param }))
}
Copy the code

Let’s talk about types here

Here we receive an optional argument, Task, which is a shared interface that we encapsulate in Types

export interface Task {
    id: number;
    name: string;
    / / agent
    processorId: number;
    projectId: number;
    / / task group
    epicId: number;
    kanbanId: number;
    // bug or task
    typeId: number;
    note: string;
}
Copy the code

All the data types defined here are returned by the back end

Partial is used to make variables in an interface optional

Now that we have achieved the task retrieval from the kanban, we also need to achieve the task retrieval from the corresponding kanban

6. useTasksSearchParams

In order to get the task data from the current kanban we also need to encapsulate a searchParams to get kanban information for the corresponding project

export const useTaskSearchParams = () = > ({ projectId: useProjectIdInUrl() })
Copy the code

Later, we will transform this hook

Second, encapsulate KanbanColumn render page

1. Unify kanban and task data

To clarify the purpose of our component, we need to use it to render the kanban for each column

The layout looks something like this. First of all, since we need to render tasks into the corresponding kanban list, we need to solve the data problem first

We’re getting the data in the KanbanColumn, and we need to be very clear here, this is our component it’s just rendering one column, we’re iterating through multiple columns, and that’s key

We get all the task data in column and filter it out using the filter method. Finally, we get the task data matching kanbanId

const { data: allTasks } = useTasks(useTasksSearchParams())
// Return three segments of data, which are arrays
consttasks = allTasks? .filter(task= > task.kanbanId === kanban.id)
Copy the code

Here’s an interesting question

We bind each column with a useTasks, which should send multiple requests. Let’s see if this is the case

Here we can see that it sent two requests, but the kanban I launched has three columns in it

So let’s add a few more columns, and let’s see

There are always only 2 requests, so why is that?

In fact, when we iterate over adding the kanbanColumns component, we only make one request, even though we bind useTask to each column

This is because, thanks to our react-Query approach, when we used useQuery, if the same queryKey made a request within 2s, those requests would be merged and only one would be made

Now that we have the Task data under each kanban, we just need to walk through the render, again using the Antd component library

2. ICONS that useTaskTypes handle for different types of tasks

Our tasks are divided into bug and task, and we have corresponding ICONS to display

Here we encapsulate a useTaskTypes under utils to get the task’s type

export const useTaskTypes = () = > {
    const client = useHttp()
    // Get all task types
    return useQuery<TaskType[]>(['taskTypes'].() = > client('taskTypes'))}Copy the code

Here we encapsulate a TaskTypeIcon widget to return the corresponding icon of the type. Here we only need to accept a taskid as an argument to determine what type the task is

// Render the image with type
const TaskTypeIcon = ({ id }: { id: number }) = > {
    const { data: taskTypes } = useTaskTypes()
    constname = taskTypes? .find(taskType= >taskType.id === id)? .name;if(! name) {return null
    }
    return <img alt={'task-icon'} src={name= = ='task' ? taskIcon : bugIcon} / >
}
Copy the code

Search function for processing tasks

1. useTasksSearchParams

In front of us has been useful to this hook, now, we need to add some code, to implement the search box logic, in the previous we used this to return the user ID object, this function can not forget oh ~

export const useTasksSearchParams = () = > {
    // Search for content
    const [param] = useUrlQueryParam([
        'name'.'typeId'.'processorId'.'tagId'
    ])
    // Get the current project ID to get kanban data
    const projectId = useProjectIdInUrl()
    // Returns an array and listens for param changes
    return useMemo(() = > ({
        projectId,
        typeId: Number(param.typeId) || undefined.processId: Number(param.processorId) || undefined.tagId: Number(param.tagId) || undefined.name: param.name
    }), [projectId, param])
}
Copy the code

The method we encapsulate here is used to return the smallest task list data. The search function we need to implement here is also implemented in the previous project search box. UseSetUrlSearchParam is used to modify the current URL address to cause data changes. The dependency in the data returned by our hook changes, causing the display content to change, so as to achieve the search effect

2. Reset button

Here’s an interesting button, clear filter, and the method request that it implements is very simple, we just reset all the data to undefined, and our clean function will say query repair is null, and then we’ll return all the data

const reset = () = > {
    setSearchParams({
        typeId: undefined.processId: undefined.tagId: undefined.name: undefined})}Copy the code

Four, kanban add, delete, change and check function

The content of this part is very similar to the previous project list. We will not go into details here, but explain the functions of the following hooks

1. useAddKanban

Then we need to deal with the kanban add and delete hook, here we need to adopt optimistic update to achieve, otherwise in the server request slow, resulting in page false death too long

As before, we use useMutation to encapsulate HTTP requests and return a mutate request or mutateAsync asynchronous request that has been processed

Here we receive a queryKey as an argument, which in this case is an array where the first element is the name of the data in the cache and the second element is its refresh dependency

export const useAddKanban = (queryKey: QueryKey) = > {
    const client = useHttp()
    // Process HTTP requests
    return useMutation(
        (params: Partial<Kanban>) = > client(`kanbans`, {
            method: "POST".data: params
        }),
        // Configure optimistic updates
        useAddConfig(queryKey)
    )
}
Copy the code

In the Config configuration, we will add the new data to the cache by destructing the array in the old element, thus implementing the changes to the data

export const useAddConfig = (queryKey: QueryKey) = > useConfig(queryKey, (target, old) = > old ? [...old, target] : [])
Copy the code

2. useDeleteKanban

Delete the kanban hook, here we use the same method, using the config we encapsulated before, for all the increase, deletion and change of the hook

// Delete kanban
export const useDeleteKanban = (queryKey: QueryKey) = > {
    const client = useHttp()
    return useMutation(
        ({ id }: { id: number }) = > client(`kanbans/${id}`, {
            method: "DELETE",
        }),
        useDeleteConfig(queryKey)
    )
}
Copy the code

The only argument received here is id, the ID of the removed kanban

5. The function of adding, deleting, modifying and checking tasks

The functions of add, delete, change and check are similar, but the parameters are not the same. Here, we take an edit function for example

We first encapsulated a hook useTasksModel to control the Modal switch

const [form] = useForm()
const { editingTaskId, editingTask, close } = useTasksModel()
// Deconstruct a task method
const { mutateAsync: editTask, isLoading: editLoading } = useEditTask(useTasksQueryKey())
// Add a method to delete the task
const { mutateAsync: deleteTask } = useDeleteTask(useTasksQueryKey())
// When you click Cancel, call close and clear the form
Copy the code

Here we expose a lot of methods for adding, deleting, modifying and checking tasks, which can be called. Here we bind onOk and onCancel methods in Modal

And there’s something remarkable about this

We are using mutateAsync asynchronous execution this time, so we need to await the execution result with await

const onCancel = () = > {
    close()
    form.resetFields()
}
const onOk = async() = > {// Get form data
    awaiteditTask({ ... editingTask, ... form.getFieldsValue() })// Close the form
    close()
}
Copy the code

📌 summary

In this article we have done the kanban page, we can learn these things

  1. Familiar with the operation of add, delete, change and check
  2. To understand theuseQueryThe use of the
  3. rightmodalComponents are better understood
  4. To understand thereact-queryAbility to optimize the number of requests

Finally, I may not be clear enough in many places, please forgive me

💌 if the article has any mistakes, or have any questions, welcome to leave a message, also welcome private letter exchange