Repository navigation
Conversation
A child of a column container is measured with the container's height as a FitContent available height. In a scroll view's content that height grows with the content, so every clean child missed the measurement cache (which matched available sizes exactly) whenever a row was added. Reuse a cached FitContent measurement when it did not fill its available height and still fits the new one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Do percent heights still work with this? For example - root (width 400, height 1000) -> container (auto) -> child (height: 50%). If we change the root height to 1200 and lay out again, i think the container will be cached to 500 instead of 600. Maybe if the feed is using a virtualised list this won't be that big of an issue? 😅 |
|
@intergalacticspacehighway Good catch, I will check that and will let you know |
A height, min/max height or flex basis in percent anywhere in the subtree is resolved against the available height, so a measurement that didn't fill its available height can still change when that height does (e.g. a 50% child in an auto-height container). Track per node whether its last layout resolved such a percentage, and only reuse an unconstrained FitContent measurement when it didn't. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A percentage below a node with a definite height (in points, or from an aspect ratio and a defined width) resolves against that height, and an absolutely positioned child doesn't affect its container's size, so neither makes a measurement depend on the available height. Stop the flag at such nodes, so containers with e.g. a 100% child inside a fixed-size or aspect-ratio box keep reusing their measurement. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@intergalacticspacehighway I added your case as a test and it failed indeed: the container stayed at 500 instead of 600. A virtualized list wouldn't have helped much here, since it affects any auto-height container with a percent-height descendant, e.g. after a rotation or a keyboard resize. Fixed in a852987 and 748729f: each node now records whether its last layout resolved a height, min/max height or flex basis percentage against its available height, and such nodes don't reuse the measurement. Percentages below a node with a definite height (points, or aspect ratio and width) and inside absolutely positioned children don't count, since they can't change the measured size, so the usual height: '100%' inside a fixed-size or aspect-ratio box still gets the speedup. Claude also ran 4,000 random trees, each laid out again at 5 root heights: the layouts now match main in all 20,000 cases (the previous version differed in 8,088). The speedup on device is about the same; the updated numbers are in the description. |
Resolves the conflict in tests/YGRelayoutTest.cpp by keeping the tests from both sides. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ment The lookup for unconstrained FitContent measurements skipped the owner size check that react#2042 added to the other cache lookups. A clean node with a fixed width and percentage padding, margin or width keeps the same available width when its owner's width changes, so a taller owner made it reuse a measurement whose percentages were resolved against the previous width. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for updating! There are still more cases though. e.g. percent gaps. root (w400, h1000) -> container (auto, rowGap: 10%) -> two 50pt children. Relayout at h1200 gives 120 instead of 122. I think instead of listing the cases that break it, we could flip the check? only reuse when the whole subtree uses values we know are safe (auto/point heights, no percent gaps, no negative margins, etc.), otherwise skip. Then a missed case is just slower, not wrong? 🤔 Rough AI untested solution. Also btw, by virtualised list I meant the general performance problem in the description, not the percent case. In feed-like layouts with a virtualised list/legendlist, would the optimisation help performance wise? just trying to understand 😅 |
Summary
computeFlexBasisForChildmeasures a child of a column container with the container's height as a FitContent available height. In a scroll view's content that height is the height of all the content, so it changes whenever a row is added. Measurements of nodes without a measure function are only reused when the available size matches exactly, so every clean child misses the cache and is measured again, together with its subtree.This reuses a cached FitContent measurement when the measured height plus margin stayed below the old available height (the constraint didn't apply) and still fits the new one. A node that fills its available height, for example a container that stretches under the
StretchFlexBasiserrata, ends up exactly at the old available height and is not reused.A percentage resolved against the available height breaks that assumption: an auto-height container with a
height: 50%child measures 500 with 1000 available and 600 with 1200, without filling either. Each node therefore records whether its last layout resolved a height, min/max height or flex basis percentage that reaches its available height, and such nodes are not reused. Percentages below a node with a definite height (in points, or from an aspect ratio and a defined width) resolve against that height, and absolutely positioned children don't affect the container's size, so neither counts.Like the other cache lookups since #2042, the reuse also requires the owner size to match when the node's style resolves a percentage against it (percentage padding, margin, width or min/max width). Otherwise a node with a fixed width keeps its available width when its owner gets narrower, and would reuse a measurement whose percentages were resolved against the old width.
Metrics
React Native 0.86 release build of a production app on a Galaxy A16, Perfetto, 4 interleaved runs per variant, each 15 flings through a long feed of text, image and video rows. Per React commit that adds feed rows:
Not reusing nodes that resolve a height percentage keeps nearly all of the gain: without that check, the commits took 3.9 ms and 5.0 ms.
Test plan
unconstrained_fit_content_height_measure_is_reused: fails on main (one extra measure).percent_height_in_auto_container_relayout_on_root_height_change: the case from review; fails without the percentage check (the container stays at 500 instead of 600).percent_height_under_definite_height_measure_is_reusedandabsolute_percent_height_child_measure_is_reused: percentages below a fixed-size or aspect-ratio box, or in an absolutely positioned child, still allow the reuse.percent_padding_follows_owner_size_on_relayout_with_taller_fit_content: a percentage padding under an owner that gets narrower and taller; fails without the owner size check (the child stays 60 tall instead of 40).display: nonechildren, wrapping and min/max sizes, each laid out again at 5 root heights. The layouts are identical to main in all 20,000 cases. The first version of this PR differed in 8,088 of them.🤖 Generated with Claude Code