‘m working on a Kanban application where I need the list of tasks to update immediately after creating a new task. I’m using MongoDB with Express and Axios, but I’m having trouble getting the list to refresh dynamically without a full page reload.
Here are the main components of my application:
1.List Component: This component fetches and displays tasks grouped by stages. It uses the useState and useEffect hooks to manage state and fetch data from the server.
type Task = {
id: string;
title: string;
description: string;
dueDate?: Date;
completed: boolean;
stageId?: string | null;
users: { id: string; name: string; avatarUrl: string }[];
createdAt: string;
updatedAt: string;
};
type TaskStage = {
id: string;
title: string;
tasks: Task[];
createdAt: string;
updatedAt: string;
};
const List = ({ children }: React.PropsWithChildren) => {
const [tasks, setTasks] = useState<Task[]>([]);
const [stages, setStages] = useState<TaskStage[]>([]);
const [isLoading, setIsLoading] = useState(true);
const {replace} = useNavigation();
// Fetch tasks and stages from MongoDB
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.get('http://localhost:3000/');
setTasks(response.data.tasks);
setStages(response.data.taskStages);
} catch (error) {
console.error('Error fetching data:', error);
} finally {
setIsLoading(false);
}
};
fetchData();
}, []);
// console.log(tasks);
// console.log(stages);
// Group tasks by stages
const tasksStages = React.useMemo(() => {
if (!tasks.length || !stages.length) {
return {
unassignedStage: [],
columns: []
};
}
const unassignedStage = tasks.filter((task: Task) => !task.stageId) ?? [];
const groupedStages = stages.map((stage: TaskStage) => ({
...stage,
tasks: tasks.filter((task: Task) => task.stageId === stage.id) ?? [],
})) ?? [];
return {
unassignedStage,
columns: groupedStages
};
}, [stages, tasks]);
const handleTaskCreate = (newTask: Task) => {
setTasks((prevTasks) => [...prevTasks, newTask]);
};
// Handle adding new card by pressing the pluss icon :
const handleAddCard = (args: { stageId: string }) => {
const path = args.stageId === 'unassigned' ? '/tasks/new' : `/tasks/new?stageId=${args.stageId}`;
replace(path); // Use navigate instead of replace
};
...
return (
<>
<KanbanBoardContainer>
<KanbanBoard onDragEnd={handleOnDragEnd}>
<KanbanColumn
id="unassigned"
title="Unassigned"
count={tasksStages.unassignedStage.length || 0}
onAddClick={() => handleAddCard({ stageId: 'unassigned' })} description={undefined}>
{tasksStages.unassignedStage.map((task: Task) => (
<KanbanItem key={task.id} id={task.id} data={{ ...task, stageId: 'unassigned' }}>
<ProjectCardMemo {...task} dueDate={String(task.dueDate) || undefined} />
</KanbanItem>
))}
{!tasksStages.unassignedStage.length && <KanbanAddCardButton onClick={() => handleAddCard({ stageId: 'unassigned' })} />}
</KanbanColumn>
{tasksStages.columns?.map((column: TaskStage) => (
<KanbanColumn
key={column.id}
id={column.id}
title={column.title}
count={column.tasks.length}
onAddClick={() => handleAddCard({ stageId: column.id })} description={undefined}>
{column.tasks.map((task: Task) => (
<KanbanItem key={task.id} id={task.id} data={task}>
<ProjectCardMemo {...task} dueDate={String(task.dueDate) || undefined} />
</KanbanItem>
))}
{!column.tasks.length && <KanbanAddCardButton onClick={() => handleAddCard({ stageId: column.id })} />}
</KanbanColumn>
))}
</KanbanBoard>
</KanbanBoardContainer>
{children}
</>
);
};
export default List;
2.TasksCreatePage Component: This component is used to create new tasks. It opens a modal form where users can input task details. Upon submission, the task is created, but I need to find a way to ensure the new task appears in the list immediately.
import { useSearchParams } from "react-router-dom";
import { useModalForm } from "@refinedev/antd";
import { useNavigation } from "@refinedev/core";
import { Form, Input, Modal } from "antd";
import axios from "axios";
const CreateTask = () => {
const [searchParams] = useSearchParams();
const { list } = useNavigation();
const { formProps, modalProps, close } = useModalForm({
action: "create",
defaultVisible: true,
});
const onSubmit = async (values: any) => {
const stageId = searchParams.get("stageId") || null;
try {
const response = await axios.post('http://localhost:3000/tasks', {
title: values.title,
stageId,
completed: false,
users: [],
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
});
if (response.status === 201) {
// Optionally show a success message here
list("tasks", "replace");
}
} catch (error) {
console.error('Error creating task:', error);
}
close();
};
return (
<Modal
{...modalProps}
onCancel={() => {
close();
list("tasks", "replace");
}}
title="Add new card"
width={512}
>
<Form {...formProps} layout="vertical" onFinish={onSubmit}>
<Form.Item label="Title" name="title" rules={[{ required: true }]}>
<Input />
</Form.Item>
</Form>
</Modal>
);
};
export default CreateTask;
3.App Component: This component manages routing in the application and includes a route to TasksCreatePage.
<Route path="/tasks" element={<List>
<Outlet/>
</List>}>
<Route path="new" element={<CreateTask/>}/>
<Route path="edit/:id" element={<EditTask/>}/>
</Route>
How can I update the List component dynamically when a new task is created using MongoDB?
I would appreciate any guidance or examples on how to achieve this functionality in a React application.