Repository navigation
Conversation
`computedFlexBasis` survives across layout calls, and the resolved-basis branch of `computeFlexBasisForChild` keeps whatever is stored so a size measured by the max-content pass carries into the definite-size pass. That guard checks whether a value exists, not which layout produced it: when an ancestor's measurement cache answers for a whole subtree the skipped max-content pass leaves a basis measured against a different available size, and a `flex: 1` node keeps its landscape height after rotating back to portrait. Track whether a stored basis was measured, and re-measure rather than retain a stale one from an earlier layout. Fixes react#2019
|
Hope this lands soon!. This is an annoying bug |
|
We hit this in a production app on React Native 0.88.0 (iOS), and #2020 fixes it for us. Our app is portrait only, except for a full-screen photo viewer (a transparent Independent repro, core React Native only (no react-native-screens), Release builds on an iPhone 17 Pro simulator, iOS 26.5: launch in landscape, rotate to portrait, rotate back to landscape.
A native probe on the stock build shows the path from #2019: the list's max-content measurement is a cache hit (826 x nan -> 826 x 122.7), then Before, stock 0.88.0: bug-0.88.0-stock-round-trip-no-modal.mp4After, with #2020 applied (the round trip, then the fix-yoga-2020-round-trip.mp4 |
Summary
Fixes #2019.
layout.computedFlexBasispersists acrossYGNodeCalculateLayoutcalls and is only cleared bymarkDirtyAndPropagateon that exact node. The resolved-flex-basis branch ofcomputeFlexBasisForChildkeeps whatever value is already stored, which is what lets a max-content measurement made earlier in the same layout survive into the definite-size pass (and is whyflex: 1inside an auto-height column means "content height"). The guard only checks whether a value exists, not which layout produced it.When a later layout at a different root size hits an ancestor's measurement cache during the max-content pass, that subtree is skipped, the child's
computedFlexBasisis never refreshed, and the definite pass retains a size measured against the previous available size. In React Native this shows up as aflex: 1Textthat keeps its landscape height after rotating back to portrait (react/react-native#58294).Fix
Record whether a stored
computedFlexBasiscame from measuring the child's content (computedFlexBasisIsMeasured). In the resolved branch, a stored value that is measured and stamped with a previous generation is treated as stale: the child takes the measure path again, which does exactly what the skipped max-content pass would have done and normally hits the child's own measurement cache. A retained resolved basis is unchanged, so "definite from the start" containers still resolveflex: 1to a basis of 0 as before, and nothing changes within a single layout.Clearing the value instead (or enabling
WebFlexBasis) is not a fix: the resolved branch then writes 0 with no measurement to follow and the child collapses to 0.Test plan
tests/YGRelayoutTest.cpp: an 8-node tree (scroll container,flex: 1column with a fixed-height sibling, two plain wrappers, aflex: 1node with a text-like measure function) laid out at 400x800, 800x400, 400x800. Asserts the measured node returns to its first height. Fails onmain, passes with this change../unit_tests Debug: all 843 tests pass. Onmainthe new test fails (portraitHeightis 100, the relaid-out node is 60) and the other 842 pass.yoga-harness/run.sh) built against this branch: two and three wrapper levels now return to their first height (they stay at the landscape height onmain); one wrapper level passes on both.