Commit 77231f4
Add fixYogaFlexBasisFitContentInMainAxis flag to avoid unnecessary re-measurement (#55897)
Summary:
X-link: react/yoga#1909
Pull Request resolved: #55897
changelog: [internal]
this change is gated.
## Problem
When Yoga computes flex basis for container children, the legacy behavior
applies a `FitContent` constraint in the **main axis**, bounding the child's
measurement by the parent's available space. This creates a dependency between
the child's flex basis and the parent's content-determined size, causing
**unnecessary re-measurement and cascading ownership clones** when siblings
change size.
### The re-measurement cascade (before fix)
```
ScrollView (overflow: scroll)
+-------------------------------+
| Content Container (auto h) |
| +---------------------------+ |
| | Item A h=200 | | <-- Item A height changes
| +---------------------------+ |
| | Item B h=300 | |
| +---------------------------+ |
| | Item C h=150 | |
| +---------------------------+ |
+-------------------------------+
|
v Content container height changes (200+300+150 = 650)
|
v FitContent(650) re-measures ALL items <-- PROBLEM
| because their flex basis was FitContent(old_height)
v
Cascading clones of the entire subtree
```
With the legacy `FitContent` in the main axis, each item's flex basis is
`min(content, parent_height)`. When Item A changes height, the content
container's height changes, which invalidates the FitContent constraint
for ALL items, triggering a full re-measurement cascade.
### After fix (MaxContent in main axis)
```
ScrollView (overflow: scroll)
+-------------------------------+
| Content Container (auto h) |
| +---------------------------+ |
| | Item A h=200 -> 250 | | <-- Item A height changes
| +---------------------------+ |
| | Item B h=300 | | <-- NOT re-measured (basis unchanged)
| +---------------------------+ |
| | Item C h=150 | | <-- NOT re-measured (basis unchanged)
| +---------------------------+ |
+-------------------------------+
|
v Content container height changes (250+300+150 = 700)
|
v Only Item A is re-measured. B and C keep their
MaxContent flex basis (independent of parent height).
```
With `MaxContent`, each item's flex basis is its intrinsic content size,
independent of the parent. Changing one item doesn't invalidate siblings.
## Solution
This diff adds a `FlexBasisFitContentInMainAxis` errata bit gated by the
`fixYogaFlexBasisFitContentInMainAxis` feature flag. When the fix is active,
flex basis measurement uses `MaxContent` (unbounded) instead of `FitContent`
for container children in the main axis.
### Three check points in `computeFlexBasisForChild`
```
computeFlexBasisForChild(parent, child)
|
|-- Check 1: Accept positive flex basis when mainAxisSize is NaN
| (fixes flexBasis:200 items in ScrollView getting height 0)
|
|-- Check 2: FitContent vs MaxContent constraint
| +--------------------------------------------------+
| | Parent type | Legacy (errata) | Fix |
| |--------------------+-----------------+-----------|
| | Auto height | FitContent | MaxContent| <-- key change
| | Definite height | FitContent | FitContent| <-- preserved
| | Scroll container | MaxContent | MaxContent| <-- unchanged
| | Text child (any) | FitContent | FitContent| <-- preserved
| +--------------------------------------------------+
|
|-- Check 3: ownerHeightForChildren fallback
(preserves percentage resolution when availableInnerHeight is NaN)
```
### Why definite-height parents keep FitContent
Yoga's default `flexShrink` is 0 (unlike CSS's default of 1). Without
FitContent, a child measured at MaxContent would get a flex basis equal
to its full content height and never shrink to fit:
```
View (height: 760) View (height: 760)
+-------------------+ +-------------------+
| Wrapper (auto h) | | Wrapper (auto h) |
| +-----------+ | | +-----------+-----|----+
| | ScrollView| | | | ScrollView| | |
| | content: | | | | content: | | |
| | 1800px | | | | 1800px | | |
| +-----------+ | | | | | |
| h=760 (bounded) | | +-----------+ | |
+-------------------+ +---|---------+-----|----+
FitContent: wrapper=760 | h=1800 (overflows!)
ScrollView can scroll MaxContent: wrapper=1800
flexShrink=0, no shrinking
ScrollView frame=1800=content
scrollable range = 0!
```
For **definite-height** parents, FitContent is safe (the parent's size is
fixed, so no re-measurement cascade). For **auto-height** parents, MaxContent
is used to avoid the cascade.
### Percentage resolution preservation (Check 3)
When MaxContent is used, `availableInnerHeight` becomes NaN. This would
break percentage-height grandchildren. Check 3 derives a definite
`ownerHeightForChildren` from the parent-provided `ownerHeight`:
```
View (height: 844)
+---------------------------+
| Wrapper (auto h) | availableInnerHeight = NaN (MaxContent)
| ownerHeight = 844 | ownerHeightForChildren = 844 (from Check 3)
| +-----+ +-----------+ |
| |h:500| |h:'50%' | | 50% resolves against 844, not NaN
| | | |= 422 | |
| +-----+ +-----------+ |
+---------------------------+
```
Children of scroll containers skip this fallback (scroll content is
intentionally unbounded).
Reviewed By: javache
Differential Revision: D94658492
fbshipit-source-id: 1587151670803ace0eae2ee91883fe4be72bfa271 parent 63796a9 commit 77231f4
27 files changed
Lines changed: 286 additions & 68 deletions
File tree
- packages/react-native
- ReactAndroid/src/main
- java/com/facebook
- react/internal/featureflags
- yoga
- jni/react/featureflags
- ReactCommon
- react
- featureflags
- nativemodule/featureflags
- renderer/components/view
- yoga/yoga
- algorithm
- enums
- scripts/featureflags
- src/private
- __tests__/utilities/__tests__
- featureflags
- specs
Lines changed: 7 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
| 7 | + | |
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| |||
384 | 384 | | |
385 | 385 | | |
386 | 386 | | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
387 | 393 | | |
388 | 394 | | |
389 | 395 | | |
| |||
Lines changed: 11 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
| 7 | + | |
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| |||
79 | 79 | | |
80 | 80 | | |
81 | 81 | | |
| 82 | + | |
82 | 83 | | |
83 | 84 | | |
84 | 85 | | |
| |||
639 | 640 | | |
640 | 641 | | |
641 | 642 | | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
642 | 652 | | |
643 | 653 | | |
644 | 654 | | |
| |||
Lines changed: 3 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
| 7 | + | |
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| |||
146 | 146 | | |
147 | 147 | | |
148 | 148 | | |
| 149 | + | |
| 150 | + | |
149 | 151 | | |
150 | 152 | | |
151 | 153 | | |
| |||
Lines changed: 3 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
| 7 | + | |
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| |||
141 | 141 | | |
142 | 142 | | |
143 | 143 | | |
| 144 | + | |
| 145 | + | |
144 | 146 | | |
145 | 147 | | |
146 | 148 | | |
| |||
Lines changed: 12 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
| 7 | + | |
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| |||
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
| 86 | + | |
86 | 87 | | |
87 | 88 | | |
88 | 89 | | |
| |||
702 | 703 | | |
703 | 704 | | |
704 | 705 | | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
705 | 716 | | |
706 | 717 | | |
707 | 718 | | |
| |||
Lines changed: 3 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
| 7 | + | |
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| |||
141 | 141 | | |
142 | 142 | | |
143 | 143 | | |
| 144 | + | |
| 145 | + | |
144 | 146 | | |
145 | 147 | | |
146 | 148 | | |
| |||
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
| |||
33 | 34 | | |
34 | 35 | | |
35 | 36 | | |
| 37 | + | |
36 | 38 | | |
37 | 39 | | |
38 | 40 | | |
| |||
Lines changed: 15 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
| 7 | + | |
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| |||
393 | 393 | | |
394 | 394 | | |
395 | 395 | | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
396 | 402 | | |
397 | 403 | | |
398 | 404 | | |
| |||
860 | 866 | | |
861 | 867 | | |
862 | 868 | | |
| 869 | + | |
| 870 | + | |
| 871 | + | |
| 872 | + | |
| 873 | + | |
863 | 874 | | |
864 | 875 | | |
865 | 876 | | |
| |||
1208 | 1219 | | |
1209 | 1220 | | |
1210 | 1221 | | |
| 1222 | + | |
| 1223 | + | |
| 1224 | + | |
1211 | 1225 | | |
1212 | 1226 | | |
1213 | 1227 | | |
| |||
Lines changed: 4 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
| 7 | + | |
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| |||
207 | 207 | | |
208 | 208 | | |
209 | 209 | | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
210 | 213 | | |
211 | 214 | | |
212 | 215 | | |
| |||
Lines changed: 5 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
| 7 | + | |
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| |||
262 | 262 | | |
263 | 263 | | |
264 | 264 | | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
265 | 269 | | |
266 | 270 | | |
267 | 271 | | |
| |||
0 commit comments