mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(site): stabilize mutation callbacks for React Compiler memoization (#24089)
This commit is contained in:
@@ -646,17 +646,24 @@ const AgentChatPage: FC = () => {
|
||||
const isRegenerateTitleDisabled = isArchived || isRegeneratingThisChat;
|
||||
const chatLastModelConfigID = chatRecord?.last_model_config_id;
|
||||
|
||||
const sendMutation = useMutation(
|
||||
// Destructure mutation results directly so the React Compiler
|
||||
// tracks stable primitives/functions instead of the whole result
|
||||
// object (TanStack Query v5 recreates it every render via object
|
||||
// spread). Keeping no intermediate variable prevents future code
|
||||
// from accidentally closing over the unstable object.
|
||||
const { isPending: isSendPending, mutateAsync: sendMessage } = useMutation(
|
||||
createChatMessage(queryClient, agentId ?? ""),
|
||||
);
|
||||
const editMutation = useMutation(editChatMessage(queryClient, agentId ?? ""));
|
||||
const interruptMutation = useMutation(
|
||||
const { isPending: isEditPending, mutateAsync: editMessage } = useMutation(
|
||||
editChatMessage(queryClient, agentId ?? ""),
|
||||
);
|
||||
const { isPending: isInterruptPending, mutateAsync: interrupt } = useMutation(
|
||||
interruptChat(queryClient, agentId ?? ""),
|
||||
);
|
||||
const deleteQueuedMutation = useMutation(
|
||||
const { mutateAsync: deleteQueuedMessage } = useMutation(
|
||||
deleteChatQueuedMessage(queryClient, agentId ?? ""),
|
||||
);
|
||||
const promoteQueuedMutation = useMutation(
|
||||
const { mutateAsync: promoteQueuedMessage } = useMutation(
|
||||
promoteChatQueuedMessage(queryClient, agentId ?? ""),
|
||||
);
|
||||
|
||||
@@ -754,9 +761,7 @@ const AgentChatPage: FC = () => {
|
||||
hasUserFixableModelProviders,
|
||||
});
|
||||
const isSubmissionPending =
|
||||
sendMutation.isPending ||
|
||||
editMutation.isPending ||
|
||||
interruptMutation.isPending;
|
||||
isSendPending || isEditPending || isInterruptPending;
|
||||
const isInputDisabled = !hasModelOptions || isArchived;
|
||||
|
||||
const handleUsageLimitError = (error: unknown): void => {
|
||||
@@ -842,7 +847,7 @@ const AgentChatPage: FC = () => {
|
||||
setPendingEditMessageId(editedMessageID);
|
||||
scrollToBottomRef.current?.();
|
||||
try {
|
||||
await editMutation.mutateAsync({
|
||||
await editMessage({
|
||||
messageId: editedMessageID,
|
||||
req: request,
|
||||
});
|
||||
@@ -873,9 +878,9 @@ const AgentChatPage: FC = () => {
|
||||
// For queued sends the WebSocket status events handle
|
||||
// clearing; for non-queued sends we clear explicitly
|
||||
// below. Clearing eagerly causes a visible cutoff.
|
||||
let response: Awaited<ReturnType<typeof sendMutation.mutateAsync>>;
|
||||
let response: Awaited<ReturnType<typeof sendMessage>>;
|
||||
try {
|
||||
response = await sendMutation.mutateAsync(request);
|
||||
response = await sendMessage(request);
|
||||
} catch (error) {
|
||||
handleUsageLimitError(error);
|
||||
throw error;
|
||||
@@ -908,10 +913,10 @@ const AgentChatPage: FC = () => {
|
||||
};
|
||||
|
||||
const handleInterrupt = () => {
|
||||
if (!agentId || interruptMutation.isPending) {
|
||||
if (!agentId || isInterruptPending) {
|
||||
return;
|
||||
}
|
||||
void interruptMutation.mutateAsync();
|
||||
void interrupt();
|
||||
};
|
||||
|
||||
const handleDeleteQueuedMessage = async (id: number) => {
|
||||
@@ -920,7 +925,7 @@ const AgentChatPage: FC = () => {
|
||||
previousQueuedMessages.filter((message) => message.id !== id),
|
||||
);
|
||||
try {
|
||||
await deleteQueuedMutation.mutateAsync(id);
|
||||
await deleteQueuedMessage(id);
|
||||
} catch (error) {
|
||||
store.setQueuedMessages(previousQueuedMessages);
|
||||
throw error;
|
||||
@@ -941,7 +946,7 @@ const AgentChatPage: FC = () => {
|
||||
store.clearStreamError();
|
||||
store.setChatStatus("pending");
|
||||
try {
|
||||
const promotedMessage = await promoteQueuedMutation.mutateAsync(id);
|
||||
const promotedMessage = await promoteQueuedMessage(id);
|
||||
// Insert the promoted message into the store and cache
|
||||
// immediately so it appears in the timeline without
|
||||
// waiting for the WebSocket to deliver it.
|
||||
@@ -990,7 +995,8 @@ const AgentChatPage: FC = () => {
|
||||
? `ssh ${workspaceAgent.name}.${workspace.name}.${workspace.owner_name}.${sshConfigQuery.data.hostname_suffix}`
|
||||
: undefined;
|
||||
|
||||
const generateKeyMutation = useMutation({
|
||||
// See mutation destructuring comment above (React Compiler).
|
||||
const { mutate: generateKey } = useMutation({
|
||||
mutationFn: () => API.getApiKey(),
|
||||
});
|
||||
|
||||
@@ -1005,7 +1011,7 @@ const AgentChatPage: FC = () => {
|
||||
const repoRoots = Array.from(gitWatcher.repositories.keys()).sort();
|
||||
const folder = repoRoots[0] ?? workspaceAgent.expanded_directory;
|
||||
|
||||
generateKeyMutation.mutate(undefined, {
|
||||
generateKey(undefined, {
|
||||
onSuccess: ({ key }) => {
|
||||
location.href = getVSCodeHref(editor, {
|
||||
owner: workspace.owner_name,
|
||||
@@ -1141,7 +1147,7 @@ const AgentChatPage: FC = () => {
|
||||
compressionThreshold={compressionThreshold}
|
||||
isInputDisabled={isInputDisabled}
|
||||
isSubmissionPending={isSubmissionPending}
|
||||
isInterruptPending={interruptMutation.isPending}
|
||||
isInterruptPending={isInterruptPending}
|
||||
isSidebarCollapsed={isSidebarCollapsed}
|
||||
onToggleSidebarCollapsed={onToggleSidebarCollapsed}
|
||||
showSidebarPanel={showSidebarPanel}
|
||||
|
||||
Reference in New Issue
Block a user