chore(lint): memoize inline style objects in list/pane components

Clears 6 react-perf/jsx-no-new-object-as-prop warnings across
synced-loader, stream-strategy-web, sortable-inline-list, file-pane, and
both draggable-list platform variants by memoizing or hoisting their
inline style objects.
This commit is contained in:
Mohamed Boudra
2026-04-24 02:48:55 +07:00
parent d50bf740ea
commit 2ee4955cbe
6 changed files with 56 additions and 32 deletions

View File

@@ -9,6 +9,8 @@ import type { DraggableListProps, DraggableRenderItemInfo } from "./draggable-li
export type { DraggableListProps, DraggableRenderItemInfo };
const SCROLL_ENABLED_FLEX_STYLE = { flex: 1 };
export function DraggableList<T>({
data,
keyExtractor,
@@ -79,7 +81,8 @@ export function DraggableList<T>({
}, []);
const showRefreshControl = Boolean(onRefresh) && (!isDragging || Boolean(refreshing));
const resolvedContainerStyle = containerStyle ?? (scrollEnabled ? { flex: 1 } : undefined);
const resolvedContainerStyle =
containerStyle ?? (scrollEnabled ? SCROLL_ENABLED_FLEX_STYLE : undefined);
const shouldShowRefreshControl = showRefreshControl && !nestable;
const ListComponent: typeof DraggableFlatList = (
nestable ? (NestableDraggableFlatList as any) : DraggableFlatList

View File

@@ -80,12 +80,15 @@ function SortableItem<T>({
const scaleTransform = isDragging ? "scale(1.02)" : "";
const combinedTransform = [baseTransform, scaleTransform].filter(Boolean).join(" ");
const style = {
transform: combinedTransform || undefined,
transition,
opacity: isDragging ? 0.9 : 1,
zIndex: isDragging ? 1000 : 1,
};
const style = useMemo(
() => ({
transform: combinedTransform || undefined,
transition,
opacity: isDragging ? 0.9 : 1,
zIndex: isDragging ? 1000 : 1,
}),
[combinedTransform, transition, isDragging],
);
const info: DraggableRenderItemInfo<T> = {
item,

View File

@@ -112,18 +112,27 @@ const CodeLine = React.memo(function CodeLine({
</View>
<Text selectable style={codeLineStyles.lineText}>
{tokens.map((token, index) => (
<Text
<CodeLineToken
key={index}
style={{ color: token.style ? (colorMap[token.style] ?? baseColor) : baseColor }}
>
{token.text}
</Text>
color={token.style ? (colorMap[token.style] ?? baseColor) : baseColor}
text={token.text}
/>
))}
</Text>
</View>
);
});
interface CodeLineTokenProps {
color: string;
text: string;
}
function CodeLineToken({ color, text }: CodeLineTokenProps) {
const style = useMemo(() => ({ color }), [color]);
return <Text style={style}>{text}</Text>;
}
const codeLineStyles = StyleSheet.create((theme) => ({
line: {
flexDirection: "row",

View File

@@ -73,12 +73,15 @@ function SortableItem<T>({
const scaleTransform = !externalDndContext && isDragging ? "scale(1.01)" : "";
const combinedTransform = [baseTransform, scaleTransform].filter(Boolean).join(" ");
const style = {
transform: combinedTransform || undefined,
transition,
opacity: externalDndContext && isDragging ? 0.3 : isDragging ? 0.9 : 1,
zIndex: isDragging ? 1000 : 1,
};
const style = useMemo(
() => ({
transform: combinedTransform || undefined,
transition,
opacity: externalDndContext && isDragging ? 0.3 : isDragging ? 0.9 : 1,
zIndex: isDragging ? 1000 : 1,
}),
[combinedTransform, transition, externalDndContext, isDragging],
);
const info: DraggableRenderItemInfo<T> = {
item,

View File

@@ -743,7 +743,7 @@ function WebStreamViewport(props: StreamRenderInput & { isMobileBreakpoint: bool
) : null}
{mountedHistoryRows}
{boundary.hasMountedHistory && boundary.hasLiveHead && boundary.historyToHeadGap > 0 ? (
<div style={{ height: boundary.historyToHeadGap, width: "100%" }} />
<HistoryToHeadSpacer height={boundary.historyToHeadGap} />
) : null}
{liveHeadRows}
{liveAuxiliary}
@@ -789,3 +789,12 @@ export function createWebStreamStrategy(input: CreateWebStreamStrategyInput): St
getBottomOffset: (metrics) => Math.max(0, metrics.contentHeight - metrics.viewportHeight),
});
}
interface HistoryToHeadSpacerProps {
height: number;
}
function HistoryToHeadSpacer({ height }: HistoryToHeadSpacerProps) {
const spacerStyle = useMemo(() => ({ height, width: "100%" as const }), [height]);
return <div style={spacerStyle} />;
}

View File

@@ -97,11 +97,8 @@ export function SyncedLoader({ size = 10, color }: { size?: number; color: strin
dotSize={dotSize}
sequenceIndex={sequenceIndex}
progress={sharedStepProgress}
style={{
position: "absolute",
left: columnIndex * (dotSize + gap),
top: rowIndex * (dotSize + gap),
}}
left={columnIndex * (dotSize + gap)}
top={rowIndex * (dotSize + gap)}
/>
);
})}
@@ -115,17 +112,15 @@ function SpinnerDot({
dotSize,
sequenceIndex,
progress,
style,
left,
top,
}: {
color: string;
dotSize: number;
sequenceIndex: number;
progress: SharedValue<number>;
style: {
position: "absolute";
left: number;
top: number;
};
left: number;
top: number;
}) {
const animatedStyle = useAnimatedStyle(() => {
const headIndex = Math.floor(progress.value) % DOT_COUNT;
@@ -153,10 +148,12 @@ function SpinnerDot({
height: dotSize,
borderRadius: dotSize / 2,
backgroundColor: color,
position: "absolute" as const,
left,
top,
},
style,
],
[animatedStyle, dotSize, color, style],
[animatedStyle, dotSize, color, left, top],
);
return <Animated.View style={dotStyle} />;