fix: stabilize useAccounts with combine and add cycle detection in useThread

- useAccounts: Replace unstable useMemo([queries]) with TanStack Query's
  combine option, which applies structural sharing (replaceEqualDeep) to
  the combined result. This prevents the accounts array from being a new
  reference on every render, breaking the cascade of unnecessary re-renders
  through useStatusQuery → useStatus → all Status components.

- useThread: Add cycle detection (visited set) to the linear mode's while
  loop that traverses inReplyTos to find the root parent. Without this,
  circular reply chains (e.g. self-replies or A→B→A) would cause an
  infinite loop that freezes the browser tab.

Co-authored-by: mkljczk <21127288+mkljczk@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-01 20:59:07 +00:00
parent e4d8c22460
commit 5b1a876326
2 changed files with 11 additions and 14 deletions

View File

@ -1,5 +1,4 @@
import { useQueries, useQueryClient } from '@tanstack/react-query';
import { useMemo } from 'react';
import { useClient } from '@/hooks/use-client';
import { queryKeys } from '@/queries/keys';
@ -10,7 +9,7 @@ const useAccounts = (accountIds: Array<string>) => {
const client = useClient();
const queryClient = useQueryClient();
const queries = useQueries({
return useQueries({
queries: accountIds.map((accountId) => ({
queryKey: queryKeys.accounts.show(accountId),
queryFn: async () => {
@ -23,18 +22,12 @@ const useAccounts = (accountIds: Array<string>) => {
},
enabled: !!accountId,
})),
combine: (results) => ({
data: results.map((result) => result.data).filter((account): account is Account => !!account),
isLoading: results.some((result) => result.isLoading),
isFetching: results.some((result) => result.isFetching),
}),
});
const accounts = useMemo(
() => queries.map((query) => query.data).filter((account): account is Account => !!account),
[queries],
);
return {
data: accounts,
isLoading: queries.some((query) => query.isLoading),
isFetching: queries.some((query) => query.isFetching),
};
};
export { useAccounts };

View File

@ -265,9 +265,13 @@ const useThread = (statusId?: string, linear?: boolean) => {
if (linear) {
let parentStatus: string = statusId;
const visited = new Set<string>([parentStatus]);
while (inReplyTos[parentStatus]) {
parentStatus = inReplyTos[parentStatus];
const next = inReplyTos[parentStatus];
if (visited.has(next)) break;
visited.add(next);
parentStatus = next;
}
const threadStatuses = [parentStatus];