#Best Practice
53 messages · Page 1 of 1 (latest)
🔎 This post has been indexed in our web forum and will be seen by search engines so other users can find it outside Discord
🕵️ Your user profile is private by default and won't be visible to users outside Discord, if you want to be visible in the web forum you can add the "Public Profile" role in id:customize
✅ You can mark a message as the answer for your post with Right click -> Apps -> Mark Solution
(if you don't see the option, try refreshing Discord with Ctrl + R)
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" };
};```
you should do this on the page
import { notFound } from 'next/navigation';
if (!task) notFound();
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" };
};
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");
}
};
no you throw it here
throw new Error("You cannot fetch other people's tasks.");
if you notice I tried rerouiting inside TaskDeleteIcon but I think there is more efficent way
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
I changed to findUnique
it also return null if not exist
yep working now, what do you think of this, should I redirect in the delete function instead of in the TaskDeleteIcon?
.
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?
yes so there is no need for redirect
so I remove the router.replace insdie the TaskDeleteIcon?
now if I delete, not found page shows up tho
what do you mean?
this is the task page
oh I think you need redirect after the delete
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;
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
so no redundant redirects will take place, if I delete from the /tasks page
keep it like this
if i redirect there, I cannot return success
I need the success message for my toaster
here
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 action
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
yes
perfect, thanks mate.