vcapretz
vcapretz•3w ago

Error throw after deleting entity in React Native

I'm building a React Native app with Expo router and my CRUD screens for entities are more or less like so: List all entities screen -> View Entity screen -> Edit screen -> Delete button. after calling the delete mutation, which in convex uses the .delete method, I use the router to dismiss back to the List screen but my "get(id)" query throws. I believe because Convex is still listening and the screen might be mounted somewhere I don't know a clear and better path to do what I'm trying to accomplish, here are some code snippets: my get single entity screen has the query:
export const get = query({
args: { id: v.id("workoutTemplates") },
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx);

if (userId === null) {
throw new Error("Not authenticated");
}

const workout = await ctx.db.get(args.id);

// THIS IS WHAT THROWS, probably because the screen is still mounted after I delete the record
if (!workout || workout.personal !== userId) {
throw new Error("Workout not found");
}

.... more code
return {};
},
});
export const get = query({
args: { id: v.id("workoutTemplates") },
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx);

if (userId === null) {
throw new Error("Not authenticated");
}

const workout = await ctx.db.get(args.id);

// THIS IS WHAT THROWS, probably because the screen is still mounted after I delete the record
if (!workout || workout.personal !== userId) {
throw new Error("Workout not found");
}

.... more code
return {};
},
});
delete mutation:
export const remove = mutation({
args: { id: v.id("workoutTemplates") },
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx);

if (userId === null) {
throw new Error("Not authenticated");
}

const workout = await ctx.db.get(args.id);

if (!workout || workout.personal !== userId) {
throw new Error("Workout not found");
}

return await ctx.db.delete(args.id);
},
});
export const remove = mutation({
args: { id: v.id("workoutTemplates") },
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx);

if (userId === null) {
throw new Error("Not authenticated");
}

const workout = await ctx.db.get(args.id);

if (!workout || workout.personal !== userId) {
throw new Error("Workout not found");
}

return await ctx.db.delete(args.id);
},
});
and in my component I'm calling it like so:
mutateDelete({ id: templateId })
.then(() => {
router.dismissTo("/personal/workouts/templates");
})
.catch((error) => {
console.log(error);

toast({
title: "Error when deleting",
preset: "error",
});
});
mutateDelete({ id: templateId })
.then(() => {
router.dismissTo("/personal/workouts/templates");
})
.catch((error) => {
console.log(error);

toast({
title: "Error when deleting",
preset: "error",
});
});
9 Replies
Convex Bot
Convex Bot•3w ago
Thanks for posting in <#1088161997662724167>. Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets. - Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.) - Use search.convex.dev to search Docs, Stack, and Discord all at once. - Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI. - Avoid tagging staff unless specifically instructed. Thank you!
vcapretz
vcapretzOP•3w ago
here's a screen recording in case it helps 😬
Noah
Noah•3w ago
Sending a message to follow this thread Having the exact same issue and unsure about how to properly handle it. My current approach to a solution is that I throw a not found in my convex query like this:
throw new ConvexError({ code: 404, message: `Entity with id "${id}" not found` });
throw new ConvexError({ code: 404, message: `Entity with id "${id}" not found` });
And then in the frontend I add an ErrorBoundary and navigate back once the 404 error is caught
export const ErrorBoundary = ({ error, retry }: ErrorBoundaryProps) => {
const router = useRouter();

if (error instanceof ConvexError && error.data.code === 404) return router.back();
return <ErrorScreen text={error.message} retry={retry} />;
};
export const ErrorBoundary = ({ error, retry }: ErrorBoundaryProps) => {
const router = useRouter();

if (error instanceof ConvexError && error.data.code === 404) return router.back();
return <ErrorScreen text={error.message} retry={retry} />;
};
But this doesn't seem to really silence the error. Not sure if this is a dev environment only issue since this is my first ever React Native app, but I'm still seeing the big red "RenderError" modal from react native
Expo Documentation
Error handling
Learn how to handle unmatched routes and errors in your app when using Expo Router.
ballingt
ballingt•3w ago
Expo seems to have made a change recently (I think?) that keeps other views mounted. This is tricky, making sure all your queries in other views are valid at all times.
ballingt
ballingt•3w ago
GitHub
convex-helpers/packages/convex-helpers/README.md at main · get-con...
A collection of useful code to complement the official packages. - get-convex/convex-helpers
ballingt
ballingt•3w ago
it returns the error as an object and should avoid the dev error popping up but this is an interesting problem, would love to hear more. It's not unique to Expo (or even to Convex, e.g. I remember deleting a resource for the page you being a UX question way back in the Rails days) but the reactivity of Convex makes handling this more fraught.
Noah
Noah•2w ago
Thanks so much for the info @Tom! Using the enhanced useQuery hook fixed the issue. Thanks for your help 🙌
vcapretz
vcapretzOP•2w ago
yeah it's definitely tricky, what I did for now was to be optimistic about my deletion working and actually navigating away before calling the mutation 😬 not sure if you have considered it @Tom, but I came from tanstack/query and having errors being returned in the queries (as well as loading state) was quite nice
ballingt
ballingt•2w ago
absolutely considered, check out the enhanced useQuery for now but looking into doing this nicely

Did you find this page helpful?