chore(lint): hoist jsx-as-prop in app components batch 3

Memoize inline JSX passed as props across combined-model-selector,
draggable-list.native, provider-diagnostic-sheet, and
workspace-setup-dialog to satisfy react-perf/jsx-no-jsx-as-prop.
This commit is contained in:
Mohamed Boudra
2026-04-24 05:40:04 +07:00
parent b4978a1ef7
commit edfdf22ea0
4 changed files with 129 additions and 88 deletions

View File

@@ -202,6 +202,49 @@ function ModelRow({
[onToggleFavorite, row.modelId, row.provider],
);
const leadingSlot = useMemo(
() => <ProviderIcon size={theme.iconSize.sm} color={theme.colors.foregroundMuted} />,
[ProviderIcon, theme.iconSize.sm, theme.colors.foregroundMuted],
);
const trailingSlot = useMemo(
() =>
onToggleFavorite && !disabled ? (
<Pressable
onPress={handleToggleFavorite}
hitSlop={8}
style={favoriteButtonStyle}
accessibilityRole="button"
accessibilityLabel={isFavorite ? "Unfavorite model" : "Favorite model"}
testID={`favorite-model-${row.provider}-${row.modelId}`}
>
{({ hovered }) => (
<Star
size={16}
color={
isFavorite
? theme.colors.palette.amber[500]
: hovered
? theme.colors.foregroundMuted
: theme.colors.border
}
fill={isFavorite ? theme.colors.palette.amber[500] : "transparent"}
/>
)}
</Pressable>
) : null,
[
onToggleFavorite,
disabled,
handleToggleFavorite,
isFavorite,
row.provider,
row.modelId,
theme.colors.palette.amber,
theme.colors.foregroundMuted,
theme.colors.border,
],
);
const showDescription = row.description && PROVIDERS_WITH_MODEL_DESCRIPTIONS.has(row.provider);
return (
@@ -212,33 +255,8 @@ function ModelRow({
disabled={disabled}
elevated={elevated}
onPress={onPress}
leadingSlot={<ProviderIcon size={theme.iconSize.sm} color={theme.colors.foregroundMuted} />}
trailingSlot={
onToggleFavorite && !disabled ? (
<Pressable
onPress={handleToggleFavorite}
hitSlop={8}
style={favoriteButtonStyle}
accessibilityRole="button"
accessibilityLabel={isFavorite ? "Unfavorite model" : "Favorite model"}
testID={`favorite-model-${row.provider}-${row.modelId}`}
>
{({ hovered }) => (
<Star
size={16}
color={
isFavorite
? theme.colors.palette.amber[500]
: hovered
? theme.colors.foregroundMuted
: theme.colors.border
}
fill={isFavorite ? theme.colors.palette.amber[500] : "transparent"}
/>
)}
</Pressable>
) : null
}
leadingSlot={leadingSlot}
trailingSlot={trailingSlot}
/>
);
}
@@ -719,6 +737,27 @@ export function CombinedModelSelector({
setView({ kind: "provider", providerId, providerLabel });
}, []);
const stickyHeader = useMemo(
() =>
view.kind === "provider" ? (
<View style={styles.level2Header}>
{!singleProviderView ? (
<ProviderBackButton
providerId={view.providerId}
providerLabel={view.providerLabel}
onBack={handleBackToAll}
/>
) : null}
<ProviderSearchInput
value={searchQuery}
onChangeText={setSearchQuery}
autoFocus={platformIsWeb}
/>
</View>
) : undefined,
[view, singleProviderView, handleBackToAll, searchQuery],
);
return (
<>
<Pressable
@@ -761,24 +800,7 @@ export function CombinedModelSelector({
desktopMinWidth={360}
desktopFixedHeight={desktopFixedHeight}
title="Select model"
stickyHeader={
view.kind === "provider" ? (
<View style={styles.level2Header}>
{!singleProviderView ? (
<ProviderBackButton
providerId={view.providerId}
providerLabel={view.providerLabel}
onBack={handleBackToAll}
/>
) : null}
<ProviderSearchInput
value={searchQuery}
onChangeText={setSearchQuery}
autoFocus={platformIsWeb}
/>
</View>
) : undefined
}
stickyHeader={stickyHeader}
>
{isContentReady ? (
<SelectorContent

View File

@@ -88,6 +88,19 @@ export function DraggableList<T>({
nestable ? (NestableDraggableFlatList as any) : DraggableFlatList
) as any;
const refreshControl = useMemo(
() =>
shouldShowRefreshControl ? (
<RefreshControl
refreshing={refreshing ?? false}
onRefresh={onRefresh}
tintColor={theme.colors.foregroundMuted}
colors={refreshColors}
/>
) : undefined,
[shouldShowRefreshControl, refreshing, onRefresh, theme.colors.foregroundMuted, refreshColors],
);
return (
<ListComponent
testID={testID}
@@ -111,16 +124,7 @@ export function DraggableList<T>({
onRelease={handleRelease}
// @ts-ignore - waitFor is supported by RNGH FlatList but missing from DraggableFlatList types
waitFor={waitFor}
refreshControl={
shouldShowRefreshControl ? (
<RefreshControl
refreshing={refreshing ?? false}
onRefresh={onRefresh}
tintColor={theme.colors.foregroundMuted}
colors={refreshColors}
/>
) : undefined
}
refreshControl={refreshControl}
/>
);
}

View File

@@ -123,6 +123,35 @@ export function ProviderDiagnosticSheet({
});
}, [fetchDiagnostic, provider, refresh]);
const headerActions = useMemo(
() => (
<Pressable
onPress={handleRefresh}
disabled={refreshInFlight}
hitSlop={8}
style={refreshButtonStyle}
accessibilityRole="button"
accessibilityLabel={
refreshInFlight ? `Refreshing ${providerLabel}` : `Refresh ${providerLabel}`
}
>
{refreshInFlight ? (
<LoadingSpinner size={theme.iconSize.sm} color={theme.colors.foregroundMuted} />
) : (
<RotateCw size={theme.iconSize.sm} color={theme.colors.foregroundMuted} />
)}
</Pressable>
),
[
handleRefresh,
refreshInFlight,
refreshButtonStyle,
providerLabel,
theme.iconSize.sm,
theme.colors.foregroundMuted,
],
);
useEffect(() => {
if (visible) {
fetchDiagnostic();
@@ -176,24 +205,7 @@ export function ProviderDiagnosticSheet({
onClose={onClose}
snapPoints={DIAGNOSTIC_SHEET_SNAP_POINTS}
scrollable={false}
headerActions={
<Pressable
onPress={handleRefresh}
disabled={refreshInFlight}
hitSlop={8}
style={refreshButtonStyle}
accessibilityRole="button"
accessibilityLabel={
refreshInFlight ? `Refreshing ${providerLabel}` : `Refresh ${providerLabel}`
}
>
{refreshInFlight ? (
<LoadingSpinner size={theme.iconSize.sm} color={theme.colors.foregroundMuted} />
) : (
<RotateCw size={theme.iconSize.sm} color={theme.colors.foregroundMuted} />
)}
</Pressable>
}
headerActions={headerActions}
>
<View style={sheetStyles.section}>
<Text style={sheetStyles.sectionTitle}>Diagnostic</Text>

View File

@@ -272,25 +272,28 @@ export function WorkspaceSetupDialog() {
[composerState, pendingAction],
);
const subtitleContent = useMemo(
() => (
<View style={styles.subtitleRow}>
{iconSource ? (
<Image source={iconSource} style={styles.projectIcon} />
) : (
<View style={styles.projectIconFallback}>
<Text style={styles.projectIconFallbackText}>{placeholderInitial}</Text>
</View>
)}
<Text style={styles.projectTitle} numberOfLines={1}>
{workspaceTitle}
</Text>
</View>
),
[iconSource, placeholderInitial, workspaceTitle],
);
if (!pendingWorkspaceSetup || !sourceDirectory) {
return null;
}
const subtitleContent = (
<View style={styles.subtitleRow}>
{iconSource ? (
<Image source={iconSource} style={styles.projectIcon} />
) : (
<View style={styles.projectIconFallback}>
<Text style={styles.projectIconFallbackText}>{placeholderInitial}</Text>
</View>
)}
<Text style={styles.projectTitle} numberOfLines={1}>
{workspaceTitle}
</Text>
</View>
);
return (
<AdaptiveModalSheet
title="Create workspace"