Best Practice
Unanswered
Russian Blue posted this in #help-forum
Russian BlueOP
I am in the current Page of an Item, there is a delete option to delete currentTask, but how can I do it so no errors come up that they were unable to fetch task with id = ".." since that task would have been deleted.
52 Replies
Russian BlueOP
Single Task Page:
import { getTaskById } from "@/services/taskServices";
import TaskEditIcon from "../_components/TaskEditIcon";
import TaskDeleteIcon from "../_components/TaskDeleteIcon";
const SingleTaskPage = async ({ params }: { params: { id: number } }) => {
const task = await getTaskById(Number(params.id));
if (!task) return;
return (
<div>
<h1>Title: {task?.title}</h1>
<h1>Description: {task?.description}</h1>
<h1>
Updated At:{" "}
{task?.updatedAt?.toLocaleDateString() !== null
? task?.updatedAt?.toLocaleDateString()
: task?.createdAt?.toLocaleDateString()}
</h1>
<TaskEditIcon task={task} />
<TaskDeleteIcon taskId={params.id} />
</div>
);
};
export default SingleTaskPage;TaskDeleteIcon:
"use client";
import { GiTrashCan } from "react-icons/gi";
import { deleteTask } from "../action";
import { toast } from "sonner";
import { useTransition } from "react";
import { usePathname, useRouter } from "next/navigation";
const TaskDeleteIcon = ({ taskId }: { taskId: number }) => {
const [isPending, startTransition] = useTransition();
const pathName = usePathname();
const router = useRouter();
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
startTransition(() => {
deleteTask(Number(taskId)).then((data) => {
router.replace("/tasks");
if (data?.error) {
toast.error(data.error);
}
if (data?.success) {
toast.success(data.success);
}
});
});
};
return (
<form onSubmit={onSubmit}>
<button type="submit" disabled={isPending}>
<GiTrashCan className="w-6 h-6 cursor-pointer hover:bg-red-500 p-[2px] rounded" />
</button>
</form>
);
};
export default TaskDeleteIcon;Action:
export const deleteTask = async (id: number) => {
const user = await currentUser();
const task = await db.task.findUnique({ where: { id } });
if (user?.id != task?.creatorId)
return { error: "You cannot delete tasks for others." };
await db.task.delete({ where: { id } });
revalidatePath("/tasks");
return { success: "Task deleted successfully" };
};@Russian Blue Single Task Page: import { getTaskById } from "@/services/taskServices";
import TaskEditIcon from "../_components/TaskEditIcon";
import TaskDeleteIcon from "../_components/TaskDeleteIcon";
const SingleTaskPage = async ({ params }: { params: { id: number } }) => {
const task = await getTaskById(Number(params.id));
if (!task) return;
return (
<div>
<h1>Title: {task?.title}</h1>
<h1>Description: {task?.description}</h1>
<h1>
Updated At:{" "}
{task?.updatedAt?.toLocaleDateString() !== null
? task?.updatedAt?.toLocaleDateString()
: task?.createdAt?.toLocaleDateString()}
</h1>
<TaskEditIcon task={task} />
<TaskDeleteIcon taskId={params.id} />
</div>
);
};
export default SingleTaskPage;
you should do this on the page
import { notFound } from 'next/navigation';
if (!task) notFound();@Russian Blue Action:
export const deleteTask = async (id: number) => {
const user = await currentUser();
const task = await db.task.findUnique({ where: { id } });
if (user?.id != task?.creatorId)
return { error: "You cannot delete tasks for others." };
await db.task.delete({ where: { id } });
revalidatePath("/tasks");
return { success: "Task deleted successfully" };
};
export const deleteTask = async (id: number) => {
const user = await currentUser();
const task = await db.task.findUnique({ where: { id } });
if (!task) {
return { error: "Task doesn't exist" }
}
if (user?.id != task?.creatorId)
return { error: "You cannot delete tasks for others." };
await db.task.delete({ where: { id } });
revalidatePath("/tasks");
return { success: "Task deleted successfully" };
};Russian BlueOP
error thrown in the console since it couldnt fetch a deleted task since it wouldn't exist after deletion:
getTaskById:
export const getTaskById = async (id: number) => {
const user = await currentUser();
try {
const task = await db.task.findFirst({
where: {
id,
},
});
if (task?.creatorId !== user?.id)
throw new Error("You cannot fetch other people's tasks.");
return task;
} catch (error) {
throw new Error("Failed to fetch task");
}
};@Russian Blue getTaskById: export const getTaskById = async (id: number) => {
const user = await currentUser();
try {
const task = await db.task.findFirst({
where: {
id,
},
});
if (task?.creatorId !== user?.id)
throw new Error("You cannot fetch other people's tasks.");
return task;
} catch (error) {
throw new Error("Failed to fetch task");
}
};
no you throw it here
throw new Error("You cannot fetch other people's tasks.");Russian BlueOP
if you notice I tried rerouiting inside TaskDeleteIcon but I think there is more efficent way
@Ray no you throw it here
`throw new Error("You cannot fetch other people's tasks.");`
Russian BlueOP
what do you mean?
findFirst return null if it is not exist
and you have this line
if (task?.creatorId !== user?.id)
throw new Error("You cannot fetch other people's tasks.");add this line
if (!task) return null@Ray findFirst return null if it is not exist
Russian BlueOP
I changed to findUnique
it also return null if not exist
Russian BlueOP
yep working now, what do you think of this, should I redirect in the delete function instead of in the TaskDeleteIcon?
@Ray add this line `if (!task) return null`
Russian BlueOP
do I do, if (!task) return redirect("/tasks")?
I don't think you need this
if the task is not exist, that button will not show right?
Russian BlueOP
right
the button is rendered as part of the task, every task has a button
yes so there is no need for redirect
Russian BlueOP
so I remove the router.replace insdie the TaskDeleteIcon?
Russian BlueOP
now if I delete, not found page shows up tho
what do you mean?
Russian BlueOP
this is the task page
@Russian Blue now if I delete, not found page shows up tho
oh I think you need redirect after the delete
Russian BlueOP
if i click delete, the task is no longer there, so the route is technically not found
export const deleteTask = async (id: number) => {
const user = await currentUser();
const task = await db.task.findUnique({ where: { id } });
if (!task) {
return { error: "Task doesn't exist" }
}
if (user?.id != task?.creatorId)
return { error: "You cannot delete tasks for others." };
await db.task.delete({ where: { id } });
revalidatePath("/tasks");
redirect('/tash');
};"use client";
import { GiTrashCan } from "react-icons/gi";
import { deleteTask } from "../action";
import { toast } from "sonner";
import { useTransition } from "react";
import { usePathname, useRouter } from "next/navigation";
const TaskDeleteIcon = ({ taskId }: { taskId: number }) => {
const [isPending, startTransition] = useTransition();
const pathName = usePathname();
const router = useRouter();
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
startTransition(() => {
deleteTask(Number(taskId)).then((data) => {
if (data?.error) {
toast.error(data.error);
}
if (data?.success) {
toast.success("Task deleted successfully");
}
});
});
};
return (
<form onSubmit={onSubmit}>
<button type="submit" disabled={isPending}>
<GiTrashCan className="w-6 h-6 cursor-pointer hover:bg-red-500 p-[2px] rounded" />
</button>
</form>
);
};
export default TaskDeleteIcon;@Ray oh I think you need redirect after the delete
Russian BlueOP
another thing that, on the tasks page, you can see all tasks, so you can delete there as well, does it also redirect or no since on the same path?
@Russian Blue another thing that, on the tasks page, you can see all tasks, so you can delete there as well, does it also redirect or no since on the same path?
it fine, if you do it with server action
it will handle it in one request and response
Russian BlueOP
so no redundant redirects will take place, if I delete from the /tasks page
Russian BlueOP
if i redirect there, I cannot return success
I need the success message for my toaster
Russian BlueOP
I was thinking to add this in my TaskDeleteIcon:
const pathName = ...
if(pathName == `tasks/${taskId}`) router.replace("/tasks");so it only redirects if the user was initially on the tasks/id page
its up to you but I would use
redirect with server actionRussian BlueOP
and will only delete and not redirect if the user on the /tasks pafe
because router.replace() will make an extra request
you could check the network tab on browser dev tools
Russian BlueOP
oh okay
do I make my success message static for my toaster then?
Russian BlueOP
perfect, thanks mate.