Skip to content

Commit b172c9a

Browse files
committed
ui: Strip useMemo/useCallback from hook utilities
Now that the React compiler is enabled (#6289), these manual memoization wrappers are redundant — the compiler handles memoization automatically. This is PR 1 of a series that removes useMemo/useCallback across the shared packages. Files changed (7 files, ~44 hook calls removed): - hooks/useQueryState.ts - components/hooks/URLState/index.tsx - useSumBy.ts - contexts/utils.ts - hooks/useCompareModeMeta.ts - useQuery.tsx - useHasProfileData.ts Also adds scripts/find-memo-candidates.sh for tracking remaining candidates in the stripping series.
1 parent 5955372 commit b172c9a

6 files changed

Lines changed: 122 additions & 130 deletions

File tree

ui/packages/shared/profile/src/contexts/utils.ts

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
// See the License for the specific language governing permissions and
1212
// limitations under the License.
1313

14-
import {useMemo} from 'react';
15-
1614
export interface LabelNameMapping {
1715
displayName: string;
1816
fullName: string;
@@ -23,21 +21,17 @@ export const transformLabelName = (labelName: string): string => {
2321
};
2422

2523
export const useLabelNameMappings = (labelNames: string[]): LabelNameMapping[] => {
26-
return useMemo(() => {
27-
return labelNames.map(name => ({
28-
displayName: transformLabelName(name),
29-
fullName: name,
30-
}));
31-
}, [labelNames]);
24+
return labelNames.map(name => ({
25+
displayName: transformLabelName(name),
26+
fullName: name,
27+
}));
3228
};
3329

3430
export const useExtractedLabelNames = (
3531
response: {labelNames?: string[]} | undefined,
3632
error: Error | undefined | null
3733
): string[] => {
38-
return useMemo(() => {
39-
return (error === undefined || error == null) && response !== undefined && response != null
40-
? response.labelNames?.filter(e => e !== '__name__') ?? []
41-
: [];
42-
}, [response, error]);
34+
return (error === undefined || error == null) && response !== undefined && response != null
35+
? response.labelNames?.filter(e => e !== '__name__') ?? []
36+
: [];
4337
};

ui/packages/shared/profile/src/hooks/useCompareModeMeta.ts

Lines changed: 29 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
// See the License for the specific language governing permissions and
1212
// limitations under the License.
1313

14-
import {useCallback} from 'react';
15-
1614
import {useURLState, useURLStateBatch} from '@parca/components';
1715

1816
/**
@@ -51,73 +49,37 @@ export const useCompareModeMeta = (): {
5149
const [compareB, setCompareB] = useURLState<string>('compare_b');
5250
const [compareAbsolute, setCompareAbsolute] = useURLState<string>('compare_absolute');
5351

54-
const closeCompareMode = useCallback(
55-
(side: 'A' | 'B') => {
56-
batchUpdates(() => {
57-
// If closing side A, swap A and B params first (keep B's data as the single view)
58-
if (side === 'A') {
59-
// Copy B to A
60-
setExpressionA(expressionB);
61-
setFromA(fromB);
62-
setToA(toB);
63-
setTimeSelectionA(timeSelectionB);
64-
setSumByA(sumByB);
65-
setMergeFromA(mergeFromB);
66-
setMergeToA(mergeToB);
67-
setSelectionA(selectionB);
68-
}
52+
const closeCompareMode = (side: 'A' | 'B'): void => {
53+
batchUpdates(() => {
54+
// If closing side A, swap A and B params first (keep B's data as the single view)
55+
if (side === 'A') {
56+
// Copy B to A
57+
setExpressionA(expressionB);
58+
setFromA(fromB);
59+
setToA(toB);
60+
setTimeSelectionA(timeSelectionB);
61+
setSumByA(sumByB);
62+
setMergeFromA(mergeFromB);
63+
setMergeToA(mergeToB);
64+
setSelectionA(selectionB);
65+
}
6966

70-
// Clear all B params
71-
setExpressionB(undefined);
72-
setFromB(undefined);
73-
setToB(undefined);
74-
setTimeSelectionB(undefined);
75-
setSumByB(undefined);
76-
setMergeFromB(undefined);
77-
setMergeToB(undefined);
78-
setSelectionB(undefined);
67+
// Clear all B params
68+
setExpressionB(undefined);
69+
setFromB(undefined);
70+
setToB(undefined);
71+
setTimeSelectionB(undefined);
72+
setSumByB(undefined);
73+
setMergeFromB(undefined);
74+
setMergeToB(undefined);
75+
setSelectionB(undefined);
7976

80-
// Clear compare mode flags
81-
setCompareA(undefined);
82-
setCompareB(undefined);
83-
setCompareAbsolute(undefined);
84-
});
85-
},
86-
[
87-
batchUpdates,
88-
// Side A setters
89-
setExpressionA,
90-
setFromA,
91-
setToA,
92-
setTimeSelectionA,
93-
setSumByA,
94-
setMergeFromA,
95-
setMergeToA,
96-
setSelectionA,
97-
// Side B values (for swapping)
98-
expressionB,
99-
fromB,
100-
toB,
101-
timeSelectionB,
102-
sumByB,
103-
mergeFromB,
104-
mergeToB,
105-
selectionB,
106-
// Side B setters
107-
setExpressionB,
108-
setFromB,
109-
setToB,
110-
setTimeSelectionB,
111-
setSumByB,
112-
setMergeFromB,
113-
setMergeToB,
114-
setSelectionB,
115-
// Compare flags
116-
setCompareA,
117-
setCompareB,
118-
setCompareAbsolute,
119-
]
120-
);
77+
// Clear compare mode flags
78+
setCompareA(undefined);
79+
setCompareB(undefined);
80+
setCompareAbsolute(undefined);
81+
});
82+
};
12183

12284
// Derive isCompareMode from flags
12385
const isCompareMode = compareA === 'true' || compareB === 'true';

ui/packages/shared/profile/src/useHasProfileData.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
// See the License for the specific language governing permissions and
1212
// limitations under the License.
1313

14-
import {useMemo} from 'react';
15-
1614
import {HasProfileDataResponse, QueryServiceClient} from '@parca/client';
1715
import {useGrpcMetadata} from '@parca/components';
1816

@@ -22,7 +20,7 @@ export const useHasProfileData = (
2220
client: QueryServiceClient
2321
): {loading: boolean; data: boolean; error: Error | any} => {
2422
const metadata = useGrpcMetadata();
25-
const metadataString = useMemo(() => JSON.stringify(metadata), [metadata]);
23+
const metadataString = JSON.stringify(metadata);
2624

2725
const {data, isLoading, error} = useGrpcQuery<HasProfileDataResponse>({
2826
key: ['hasProfileData', metadataString],

ui/packages/shared/profile/src/useQuery.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
// See the License for the specific language governing permissions and
1212
// limitations under the License.
1313

14-
import {useMemo} from 'react';
15-
1614
import {RpcError} from '@protobuf-ts/runtime-rpc';
1715

1816
import {QueryRequest_ReportType, QueryResponse, QueryServiceClient} from '@parca/client';
@@ -50,9 +48,7 @@ export const useQuery = (
5048
const {skip = false} = options ?? {};
5149
const metadata = useGrpcMetadata();
5250

53-
const protoFiltersKey = useMemo(() => {
54-
return JSON.stringify(options?.protoFilters ?? []);
55-
}, [options?.protoFilters]);
51+
const protoFiltersKey = JSON.stringify(options?.protoFilters ?? []);
5652

5753
const {data, isLoading, error, refetch} = useGrpcQuery<QueryResponse | undefined>({
5854
key: [

ui/packages/shared/profile/src/useSumBy.ts

Lines changed: 26 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
// See the License for the specific language governing permissions and
1212
// limitations under the License.
1313

14-
import {useCallback, useMemo, useState} from 'react';
14+
import {useState} from 'react';
1515

1616
import {QueryServiceClient} from '@parca/client';
1717
import {DateTimeRange} from '@parca/components';
@@ -84,46 +84,40 @@ export const useSumBySelection = (
8484
}
8585
}
8686

87-
const setSumBy = useCallback(
88-
(sumBy: string[]) => {
89-
setUserSelectedSumBy(prev => {
90-
if (profileType == null) {
91-
return prev;
92-
}
93-
94-
return {
95-
...prev,
96-
[profileType.toString()]: sumBy,
97-
};
98-
});
99-
},
100-
[setUserSelectedSumBy, profileType]
101-
);
87+
const setSumBy = (sumBy: string[]): void => {
88+
setUserSelectedSumBy(prev => {
89+
if (profileType == null) {
90+
return prev;
91+
}
10292

103-
const {defaultSumBy} = useDefaultSumBy(profileType, labelNamesLoading, labels);
93+
return {
94+
...prev,
95+
[profileType.toString()]: sumBy,
96+
};
97+
});
98+
};
10499

105-
const sumBy = useMemo(() => {
106-
// For smoother UX, return draftSumBy first if available during loading
107-
// as this must be recently computed with the draft time range labels.
108-
if (labelNamesLoading && draftSumBy !== undefined) {
109-
return draftSumBy;
110-
}
100+
const {defaultSumBy} = useDefaultSumBy(profileType, labelNamesLoading, labels);
111101

102+
// For smoother UX, return draftSumBy first if available during loading
103+
// as this must be recently computed with the draft time range labels.
104+
let sumBy: string[] | undefined;
105+
if (labelNamesLoading && draftSumBy !== undefined) {
106+
sumBy = draftSumBy;
107+
} else {
112108
// Prefer non-empty URL default over auto-computed default to avoid a
113109
// one-render race where defaultSumBy overwrites the default value from upstream.
114110
const hasExplicitDefault = defaultValue != null && defaultValue.length > 0;
115-
let result =
111+
sumBy =
116112
userSelectedSumBy[profileType?.toString() ?? ''] ??
117113
(hasExplicitDefault ? defaultValue : undefined) ??
118114
defaultSumBy ??
119115
DEFAULT_EMPTY_SUM_BY;
120116

121117
if (profileType?.delta !== true) {
122-
result = DEFAULT_EMPTY_SUM_BY;
118+
sumBy = DEFAULT_EMPTY_SUM_BY;
123119
}
124-
125-
return result;
126-
}, [userSelectedSumBy, profileType, defaultSumBy, labelNamesLoading, draftSumBy, defaultValue]);
120+
}
127121

128122
return [
129123
sumBy,
@@ -139,9 +133,7 @@ export const useDefaultSumBy = (
139133
labelNamesLoading: boolean,
140134
labels: string[] | undefined
141135
): {defaultSumBy: string[] | undefined; isLoading: boolean} => {
142-
const defaultSumBy = useMemo(() => {
143-
return getDefaultSumBy(profileType, labels);
144-
}, [profileType, labels]);
136+
const defaultSumBy = getDefaultSumBy(profileType, labels);
145137

146138
return {defaultSumBy, isLoading: labelNamesLoading};
147139
};
@@ -170,11 +162,7 @@ const getSumByFromParam = (param: string | string[] | undefined): string[] | und
170162
};
171163

172164
export const useSumByFromParams = (param: string | string[] | undefined): string[] | undefined => {
173-
const sumBy = useMemo(() => {
174-
return getSumByFromParam(param);
175-
}, [param]);
176-
177-
return sumBy;
165+
return getSumByFromParam(param);
178166
};
179167

180168
export const sumByToParam = (sumBy: string[] | undefined): string | string[] | undefined => {
@@ -219,9 +207,7 @@ export const useSumBy = (
219207
defaultValue
220208
);
221209

222-
const labels = useMemo(() => {
223-
return result.response?.labelNames === undefined ? [] : result.response.labelNames;
224-
}, [result]);
210+
const labels = result.response?.labelNames === undefined ? [] : result.response.labelNames;
225211

226212
const [sumBySelection, setSumByInternal, {isLoading}] = useSumBySelection(
227213
profileType,
@@ -259,9 +245,7 @@ export const useDraftSumBy = (
259245
timeRange.getToMs()
260246
);
261247

262-
const labels = useMemo(() => {
263-
return result.response?.labelNames === undefined ? [] : result.response.labelNames;
264-
}, [result]);
248+
const labels = result.response?.labelNames === undefined ? [] : result.response.labelNames;
265249

266250
const {defaultSumBy, isLoading} = useDefaultSumBy(profileType, labelNamesLoading, labels);
267251

ui/scripts/find-memo-candidates.sh

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/usr/bin/env bash
2+
# Copyright 2026 The Parca Authors
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# Lists files that are candidates for stripping useMemo/useCallback
16+
# after the React compiler is enabled, excluding files with 'use no memo'.
17+
18+
set -euo pipefail
19+
20+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
21+
SHARED="$ROOT/packages/shared"
22+
23+
# Find all .ts/.tsx files with useMemo or useCallback
24+
candidates=$(grep -rl --include='*.ts' --include='*.tsx' \
25+
-E 'useMemo|useCallback' \
26+
"$SHARED" \
27+
| grep -v '__tests__' \
28+
| grep -v '\.test\.' \
29+
| grep -v '\.spec\.' \
30+
| grep -v 'benchdata' \
31+
| grep -v 'testdata' \
32+
| sort)
33+
34+
excluded=0
35+
included=0
36+
total_hooks=0
37+
38+
echo "=== useMemo/useCallback candidates (excluding 'use no memo' files) ==="
39+
echo ""
40+
41+
while IFS= read -r file; do
42+
if grep -q 'use no memo' "$file" 2>/dev/null; then
43+
excluded=$((excluded + 1))
44+
continue
45+
fi
46+
47+
rel="${file#"$ROOT"/}"
48+
count=$(grep -cE '\buseMemo\b|\buseCallback\b' "$file" || true)
49+
total_hooks=$((total_hooks + count))
50+
included=$((included + 1))
51+
printf " %-4d %s\n" "$count" "$rel"
52+
done <<<"$candidates"
53+
54+
echo ""
55+
echo "--- Summary ---"
56+
echo "Candidate files: $included"
57+
echo "Hook calls: $total_hooks"
58+
echo "Excluded ('use no memo'): $excluded"

0 commit comments

Comments
 (0)