Optimistic Updates with TRPC Hook
I've made a hook with TRPC and am trying to create optimistic updates.. simplified below
and then i consume it here:
export function useComments(urlId: string) {
const [comments, setComments] = useState([] as urlComments[]);
const [error, setError] = useState(null);
const trpcContext = trpc.useContext();
// works
const { data, isLoading, isError } = trpc.comments.getComments.useQuery(
urlId,
{
onSuccess: (data) => {
setComments(data);
},
}
);
// this is where the optimistic update happens
const commentMutation = trpc.comments.createComment.useMutation({
onMutate: async (newComment) => {
const previousComments = [...comments];
const optimisticComment = {
...newComment,
id: `temp-${Date.now()}`,
type: "TEMP",
};
setComments((oldComments) => [optimisticComment, ...previousComments]);
/// above line didnt work, below also no workie
trpcContext.timelines.getTimeline.setData("undefined", (data) => {
return [optimisticComment, ...previousComments];
});
return { previousComments }; // Return the snapshot for potential rollback
},
....
});
const addComment = (commentData) => {
commentMutation.mutate({ urlId, ...commentData });
};
return {
comments,
addComment,
error,
isLoading,
isError,
};
}
export function useComments(urlId: string) {
const [comments, setComments] = useState([] as urlComments[]);
const [error, setError] = useState(null);
const trpcContext = trpc.useContext();
// works
const { data, isLoading, isError } = trpc.comments.getComments.useQuery(
urlId,
{
onSuccess: (data) => {
setComments(data);
},
}
);
// this is where the optimistic update happens
const commentMutation = trpc.comments.createComment.useMutation({
onMutate: async (newComment) => {
const previousComments = [...comments];
const optimisticComment = {
...newComment,
id: `temp-${Date.now()}`,
type: "TEMP",
};
setComments((oldComments) => [optimisticComment, ...previousComments]);
/// above line didnt work, below also no workie
trpcContext.timelines.getTimeline.setData("undefined", (data) => {
return [optimisticComment, ...previousComments];
});
return { previousComments }; // Return the snapshot for potential rollback
},
....
});
const addComment = (commentData) => {
commentMutation.mutate({ urlId, ...commentData });
};
return {
comments,
addComment,
error,
isLoading,
isError,
};
}
export default function Comments({
}: Props): JSX.Element {
const { comments } = useComments(id);
// RERENDER NOT HAPPENING!!
console.log({comments});
return (
<>
{comments &&
comments.map((item) => (
<Item item={item} />
))}
<>
);
}
export default function Comments({
}: Props): JSX.Element {
const { comments } = useComments(id);
// RERENDER NOT HAPPENING!!
console.log({comments});
return (
<>
{comments &&
comments.map((item) => (
<Item item={item} />
))}
<>
);
}
3 Replies
resolved it by lifting state
Would be curious to see your working implementation as I have a similar use case
export function useComments(urlId: string) {
const [error, setError] = useState(null);
const trpcContext = trpc.useContext();
const session = useSession();
const {
data: comments,
isLoading,
isError,
} = trpc.timelines.getTimeline.useQuery(urlId);
const commentMutation = trpc.timelines.createUserComment.useMutation({
onMutate: async (newComment: CommentFormSchema) => {
const actualUser = getActualUserFromSession(session);
const previousComments =
trpcContext.timelines.getTimeline.getData(newComment.urlId) || [];
const optimisticComment = {
...newComment,
...moreData
};
trpcContext.timelines.getTimeline.setData(newComment.urlId, (data) => {
return [optimisticComment, ...previousComments];
});
return { previousComments }; // Return the snapshot for potential rollback
},
onError: (error, newComment, context) => {
setError(error);
trpcContext.timelines.getTimeline.getData(newComment.urlId) || [];
},
onSettled: () => {
trpcContext.timelines.getTimeline.invalidate(urlId);
},
});
const addComment = (commentData: CommentFormSchema) => {
commentMutation.mutate(commentData);
};
return {
comments,
addComment,
error,
isLoading,
isError,
};
}
export function useComments(urlId: string) {
const [error, setError] = useState(null);
const trpcContext = trpc.useContext();
const session = useSession();
const {
data: comments,
isLoading,
isError,
} = trpc.timelines.getTimeline.useQuery(urlId);
const commentMutation = trpc.timelines.createUserComment.useMutation({
onMutate: async (newComment: CommentFormSchema) => {
const actualUser = getActualUserFromSession(session);
const previousComments =
trpcContext.timelines.getTimeline.getData(newComment.urlId) || [];
const optimisticComment = {
...newComment,
...moreData
};
trpcContext.timelines.getTimeline.setData(newComment.urlId, (data) => {
return [optimisticComment, ...previousComments];
});
return { previousComments }; // Return the snapshot for potential rollback
},
onError: (error, newComment, context) => {
setError(error);
trpcContext.timelines.getTimeline.getData(newComment.urlId) || [];
},
onSettled: () => {
trpcContext.timelines.getTimeline.invalidate(urlId);
},
});
const addComment = (commentData: CommentFormSchema) => {
commentMutation.mutate(commentData);
};
return {
comments,
addComment,
error,
isLoading,
isError,
};
}