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
- encapsulation
KanbanColumn
To lay out the page - Write a large number of
custom hook
To process kanban data - right
useQuery
Have further understanding - using
filter
Achieve 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
- Familiar with the operation of add, delete, change and check
- To understand the
useQuery
The use of the - right
modal
Components are better understood - To understand the
react-query
Ability 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