Version: 3.3.9 (also reproduced on 3.3.3), web build (react-native-web / Expo web), Chrome.
ScrollAdjust.tsx pads the content container to make room for a downward scroll adjustment and reverts it on the next animation frame, but only if the inline style still equals the string it wrote:
const temporaryPaddingEnd = `${(currentPaddingEnd || 0) + pad}px`;
temporaryPaddingRef.current = { baseline: baselinePaddingEnd, value: temporaryPaddingEnd };
contentNode.style[axis.paddingEndProp] = temporaryPaddingEnd;
// ...
requestAnimationFrame(() => {
if (contentNode.style[axis.paddingEndProp] === temporaryPadding?.value) { // fails
contentNode.style[axis.paddingEndProp] = temporaryPadding.baseline;
}
});
(currentPaddingEnd || 0) + pad is an unrounded float, so the written string is e.g. "258.15000000000003px", while the browser stores and serializes it as "258.15px":
el.style.paddingBottom = '258.15000000000003px';
el.style.paddingBottom; // "258.15px" -> the guard never matches
The padding therefore stays forever. The result is permanent empty scrollable space below the last item that the list itself knows nothing about (getContentSize() does not include it).
Repro: a vertical list with alignItemsAtEnd, maintainScrollAtEnd, maintainVisibleContentPosition: {data: true, size: true}, initialScrollAtEnd, mounted at the end; then let already-measured rows shrink (we simulate images whose real dimensions arrive ~2 s after mount). Intermittent - it only bites when the computed value does not round-trip through CSS serialization.
Version: 3.3.9 (also reproduced on 3.3.3), web build (react-native-web / Expo web), Chrome.
ScrollAdjust.tsx pads the content container to make room for a downward scroll adjustment and reverts it on the next animation frame, but only if the inline style still equals the string it wrote:
(currentPaddingEnd || 0) + padis an unrounded float, so the written string is e.g."258.15000000000003px", while the browser stores and serializes it as"258.15px":The padding therefore stays forever. The result is permanent empty scrollable space below the last item that the list itself knows nothing about (getContentSize() does not include it).
Repro: a vertical list with
alignItemsAtEnd,maintainScrollAtEnd,maintainVisibleContentPosition: {data: true, size: true},initialScrollAtEnd, mounted at the end; then let already-measured rows shrink (we simulate images whose real dimensions arrive ~2 s after mount). Intermittent - it only bites when the computed value does not round-trip through CSS serialization.