In this lesson, we will replace static task data with the list of tasks retrieved from API server.
Open src/context/tasks/types.ts
in VS Code.
Let's add capability to fetch tasks.
export enum TaskListAvailableAction {
// Add actions for fetching tasks from server
FETCH_TASKS_REQUEST = "FETCH_TASKS_REQUEST",
FETCH_TASKS_SUCCESS = "FETCH_TASKS_SUCCESS",
FETCH_TASKS_FAILURE = "FETCH_TASKS_FAILURE",
CREATE_TASK_REQUEST = "CREATE_TASK_REQUEST",
CREATE_TASK_SUCCESS = "CREATE_TASK_SUCCESS",
CREATE_TASK_FAILURE = "CREATE_TASK_FAILURE",
REORDER_TASKS = "REORDER_TASKS",
}
export type TaskActions =
| { type: TaskListAvailableAction.REORDER_TASKS; payload: ProjectData }
| { type: TaskListAvailableAction.FETCH_TASKS_REQUEST }
| { type: TaskListAvailableAction.FETCH_TASKS_SUCCESS; payload: ProjectData }
| { type: TaskListAvailableAction.FETCH_TASKS_FAILURE; payload: string }
| { type: TaskListAvailableAction.CREATE_TASK_REQUEST }
| { type: TaskListAvailableAction.CREATE_TASK_SUCCESS }
| { type: TaskListAvailableAction.CREATE_TASK_FAILURE; payload: string };
We will also update the reducer to update the state:
export const taskReducer: Reducer<TaskListState, TaskActions> = (
state = initialState,
action
) => {
switch (action.type) {
// Update reducer to handle the actions dispatched on fetching tasks.
case TaskListAvailableAction.FETCH_TASKS_REQUEST:
return { ...state, isLoading: true };
case TaskListAvailableAction.FETCH_TASKS_SUCCESS:
return { ...state, isLoading: false, projectData: action.payload };
case TaskListAvailableAction.FETCH_TASKS_FAILURE:
return {
...state,
isLoading: false,
isError: true,
errorMessage: action.payload,
};
case TaskListAvailableAction.CREATE_TASK_REQUEST:
return { ...state, isLoading: true };
case TaskListAvailableAction.CREATE_TASK_SUCCESS:
return { ...state, isLoading: false };
case TaskListAvailableAction.CREATE_TASK_FAILURE:
return {
...state,
isLoading: false,
isError: true,
errorMessage: action.payload,
};
case TaskListAvailableAction.REORDER_TASKS:
return { ...state, isLoading: false, projectData: action.payload };
default:
return state;
}
};
Save the file.
Switch to src/context/tasks/action.tsx
We will now create a function called refreshTasks
which will trigger a request to API server and fetches the task list for a given projectID
.
export const refreshTasks = async (
dispatch: TasksDispatch,
projectID: string
) => {
const token = localStorage.getItem("authToken") ?? "";
try {
dispatch({ type: TaskListAvailableAction.FETCH_TASKS_REQUEST });
const response = await fetch(
`${API_ENDPOINT}/projects/${projectID}/tasks`,
{
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
}
);
if (!response.ok) {
throw new Error("Failed to create project");
}
// extract the response body as JSON data
const data = await response.json();
dispatch({
type: TaskListAvailableAction.FETCH_TASKS_SUCCESS,
payload: data,
});
console.dir(data);
} catch (error) {
console.error("Operation failed:", error);
dispatch({
type: TaskListAvailableAction.FETCH_TASKS_FAILURE,
payload: "Unable to load tasks",
});
}
};
Now, we will trigger this fetchTasks
when the ProjectDetails
component gets mounted using useEffect
hook. Switch to src/pages/project_details/ProjectDetails.tsx
.
Let's import useTasksDispatch
and useTasksState
import { useTasksDispatch, useTasksState } from "../../context/tasks/context";
We will also import refreshTasks
from actions.ts
.
import { refreshTasks } from "../../context/tasks/actions";
Now, we will make use of the useEffect
hook to trigger refreshTasks
. We can extract the project id
from url using the useParams
hook from react-router-dom
.
import React, { useEffect } from "react";
import { Link, useParams } from "react-router-dom";
import { useTasksDispatch, useTasksState } from "../../context/tasks/context";
import DragDropList from "./DragDropList";
import { refreshTasks } from "../../context/tasks/actions";
import { useProjectsState } from "../../context/projects/context";
const ProjectDetails = () => {
const tasksState = useTasksState();
const taskDispatch = useTasksDispatch();
const projectState = useProjectsState();
let { projectID } = useParams();
useEffect(() => {
if (projectID) refreshTasks(taskDispatch, projectID);
}, [projectID, taskDispatch]);
const selectedProject = projectState?.activeProject;
if (!selectedProject) {
return <>No such Project!</>;
}
if (tasksState.isLoading) {
return <>Loading...</>;
}
return (
<>
<div className="flex justify-between">
<h2 className="text-2xl font-medium tracking-tight text-slate-700">
{selectedProject.name}
</h2>
<Link to={`tasks/new`}>
<button
id="newTaskBtn"
className="rounded-md bg-blue-600 px-4 py-2 m-2 text-sm font-medium text-white hover:bg-opacity-95 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75"
>
New Task
</button>
</Link>
</div>
<div className="grid grid-cols-1 gap-2">
<DragDropList data={tasksState.projectData} />
</div>
</>
);
};
export default ProjectDetails;
Save the file. Now, the tasks for a project will get listed properly. And they can be moved around the lists as well. But if you reload the page, after moving a task from one list to another, you can see, the task is still been shown at it's previous position. We will fix this later.
See you in the next lesson.