Optimistic Updates with TRPC Hook

I've made a hook with TRPC and am trying to create optimistic updates.. simplified below
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,
};
}
and then i consume it here:
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
fotoflo
fotoflo6mo ago
resolved it by lifting state
Ben
Ben6mo ago
Would be curious to see your working implementation as I have a similar use case
fotoflo
fotoflo6mo ago
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,
};
}
oh but the actual solution was i had to lift the state for some reason... it wasn't causing refresh cc @Ben
Want results from more Discord servers?
Add your server