fix(layout): enforce height on items in horizontal lists#2205
fix(layout): enforce height on items in horizontal lists#2205shopify-github-actions-access[bot] wants to merge 1 commit intomainfrom
Conversation
In horizontal FlashList, items had no enforced height dimension, causing nested ScrollViews to receive invalid layout metrics (infinity/NaN values). This happened because enforcedHeight was never set in LinearLayoutManager, so ViewHolder rendered items without any height constraint. The fix adds enforcedHeight for horizontal layouts (symmetric with how vertical layouts enforce width) and sets item height to the bounded size when the container height is known. This gives nested ScrollViews a definite height constraint to work within. Fixes #1924
There was a problem hiding this comment.
Pull request overview
This PR fixes incorrect cross-axis sizing for horizontal FlashList items by enforcing a bounded height, preventing nested ScrollView children from receiving invalid layout constraints (Infinity/NaN) and failing to render.
Changes:
- Forces measured item height to the horizontal bounded container size when available (
boundedSize). - Introduces
enforcedHeight = truefor horizontal layouts (symmetric withenforcedWidthin vertical layouts).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -86,6 +87,7 @@ export class RVLinearLayoutManagerImpl extends RVLayoutManager { | |||
| layout.height = this.getEstimatedHeight(index); | |||
There was a problem hiding this comment.
estimateLayout now sets enforcedHeight = this.horizontal, which makes layout.height a hard constraint in horizontal mode. However, layout.height is still initialized to getEstimatedHeight(index) even when the cross-axis container size is already known (this.hasSize). This can end up enforcing the default estimate (e.g. 200) instead of the bounded container height, which undermines the intent of providing a definite height constraint early (before real measurements). Consider setting layout.height to this.boundedSize when this.horizontal && this.hasSize, mirroring how vertical mode uses boundedSize for width.
| layout.height = this.getEstimatedHeight(index); | |
| layout.height = this.horizontal && this.hasSize | |
| ? this.boundedSize | |
| : this.getEstimatedHeight(index); |
| layout.width = this.horizontal ? dimensions.width : this.boundedSize; | ||
| layout.isHeightMeasured = true; | ||
| layout.isWidthMeasured = true; | ||
| layout.height = dimensions.height; | ||
| layout.height = | ||
| this.horizontal && this.hasSize ? this.boundedSize : dimensions.height; | ||
| } |
There was a problem hiding this comment.
This logic relies on this.hasSize to decide whether to force horizontal item height to boundedSize, but hasSize is only set in the constructor and is not updated when boundedSize changes in updateLayoutParams. If windowSize.height transitions from 0 to a real value after mount (common during initial layout), hasSize can remain false and this branch will never apply, leaving horizontal items without the intended enforced height. Consider updating this.hasSize = this.boundedSize > 0 inside updateLayoutParams whenever boundedSize is recalculated.
| layout.height = this.getEstimatedHeight(index); | ||
| layout.isWidthMeasured = !this.horizontal; | ||
| layout.enforcedWidth = !this.horizontal; | ||
| layout.enforcedHeight = this.horizontal; | ||
| } |
There was a problem hiding this comment.
The new horizontal-only behavior (enforcedHeight and height overriding to boundedSize) isn't covered by the current LinearLayoutManager unit tests. Adding assertions for layouts[i].enforcedHeight === true and that layouts[i].height equals the horizontal bounded container height (when windowSize.height is known) would help prevent regressions for the nested-ScrollView scenario this PR fixes.
|
@agent review the feedback left by copilot |
|
Claude finished @naqvitalha's task in 1m 23s —— View job Review of Copilot's feedback on PR #2205
I reviewed all three Copilot comments against the actual code. Here's my assessment: 1.
|
| # | Copilot Comment | Verdict | Severity |
|---|---|---|---|
| 1 | hasSize not updated in updateLayoutParams |
Valid bug — the fix won't work when initial height is 0 | High |
| 2 | estimateLayout should use boundedSize |
Valid improvement — avoids extra layout pass | Low |
| 3 | Missing test coverage | Valid — should add assertions for new behavior | Medium |
Recommendation: Comment 1 should be addressed before merging — it's a correctness issue that can make the fix ineffective in a common scenario (initial layout with 0 height). Comments 2 and 3 are nice-to-haves that would strengthen the PR.
|
@agent we do assume that once hasSize is set, it's always true. |
|
Claude finished @naqvitalha's task in 41s —— View job
Updated AssessmentThanks for the clarification. With the assumption that Looking at the code:
So if a horizontal list mounts with Given your assumption that once set it's always true: the fix is simply to also set A one-line addition after line 42 would do it: this.hasSize = this.hasSize || this.boundedSize > 0;This preserves the "never goes back to false" invariant while handling the late-arriving size. Without it, the PR's fix is ineffective when The other two Copilot suggestions (using |
Description
In horizontal FlashList, items had no enforced height dimension (
enforcedHeightwas never set inLinearLayoutManager). This caused nested ScrollViews to receive invalid layout metrics with infinity/NaN values, making them unable to render properly.The fix adds
enforcedHeight = truefor horizontal layouts (symmetric with how vertical layouts useenforcedWidth) and sets item height to the bounded container size when known. This gives nested ScrollViews a definite height constraint.Fixes #1924
Reviewers' hat-rack 🎩
estimateLayoutmirrors the existingenforcedWidth = !this.horizontalpatternprocessLayoutInfochange ensures measured heights respect the bounded size for horizontal lists (similar to how width usesboundedSizefor vertical lists)normalizeLayoutHeightspath, unaffected by thehasSizeguard)Test plan
yarn test)yarn type-check)yarn lint)