In the previous chapter, Recursive Layout Algorithm, we saw how the engine visits every single node, negotiates sizes, and calculates coordinates.
That sounds great for a static page, but what if you have an animation running at 60 frames per second? Or a scrollable list with 1,000 items?
If we re-run the entire math equation for every pixel shift, the CPU will choke.
This chapter introduces the Layout Caching System. It is the memory that allows native-ts to say: "I already calculated this part, so I'm going to skip the math and just give you the answer."
Imagine a waiter in a restaurant.
Now, Customer B asks the same question.
In layout terms:
layoutNode function.width and height.isDirty)The most basic optimization is the Dirty Flag.
When you create a node or update its style, it is marked as Dirty. This means "I have changed, my previous calculations are invalid."
When you change a style property, the setter automatically sets this flag.
// yoga-layout/index.ts
setWidth(v: number): void {
this.style.width = pointValue(v);
// Mark myself as needing a recalculation
this.markDirty();
}
When the layout engine visits a node, the first thing it checks is: Is this node dirty?
If isDirty is false, and the context hasn't changed, the engine skips the entire recursion for that branch. It trusts the existing numbers in node.layout.
Sometimes, a node is NOT dirty, but we still can't just return the current width/height.
Why? Because Flexbox is a negotiation. A parent might ask a child to measure itself multiple times in a single pass:
To handle this, native-ts uses a sophisticated cache that stores Inputs and Outputs.
_cIn)The cache remembers the exact conditions under which the calculation happened:
_cOut)The cache remembers the result:
Here is what happens when a Parent asks a Child to layout.
Let's look under the hood in yoga-layout/index.ts. The implementation uses raw typed arrays (Float64Array) for speed, but the logic is straightforward.
At the very top of the layoutNode function, before doing any work, we try to exit early.
// yoga-layout/index.ts - Inside layoutNode()
// Check if we have seen these EXACT inputs before
if (node._cN > 0 && !node.isDirty_) {
// Loop through cache slots (native-ts stores up to 4 past results)
for (let i = 0; i < node._cN; i++) {
// helper function checks if cached inputs == current inputs
if (cacheMatches(node, i, availableWidth, widthMode...)) {
// CACHE HIT! Restore the result and stop.
node.layout.width = node._cOut[i * 2];
node.layout.height = node._cOut[i * 2 + 1];
return;
}
}
}
If we miss the cache, we proceed with the Recursive Layout Algorithm. Once finished, we save the result so the next time we are asked, we can skip the work.
// yoga-layout/index.ts
// After layout is finished...
cacheWrite(
node,
availableWidth, availableHeight, // The Question (Inputs)
widthMode, heightMode,
// ... other context
);
The cacheWrite function pushes these values into _cIn (Inputs) and _cOut (Outputs).
_generation)There is one final safety mechanism: The Generation ID.
When you call .calculateLayout() on the root, the engine increments a global counter called _generation.
// Global counter for the entire system
let _generation = 0;
node.calculateLayout() {
_generation++;
// ... start recursion
}
When a node writes to its cache, it stamps the entry with the current _generation.
Why is this needed? If you scroll a list, new items appear. The cache from the previous frame might be invalid because the tree structure itself changed. The generation ID ensures that we only use cache entries that are relevant to the current calculation pass.
Let's verify this behavior with a mental walkthrough.
Setup:
Text node inside a Box.Text takes 5ms to measure (slow!).Frame 1:
calculateLayout starts. Generation = 1.Box asks Text to measure.Text is dirty. Cache empty.Text saves result to cache (Gen 1).
Frame 2 (We resize the Box slightly, but Text is unchanged):
calculateLayout starts. Generation = 2.Box asks Text to measure.Text is NOT dirty.Text checks cache. It sees an entry for the same available width.
This caching system is what allows native-ts (and React Native) to perform complex layouts efficiently in real-time.
The Layout Caching System prevents unnecessary work.
Now that we understand how the layout engine positions boxes efficiently, we need to switch gears. In a text editor or file explorer, we don't just layout boxes; we need to find files quickly.
In the next chapter, we will explore the search engine behind the project.
Next Chapter: Fuzzy File Index
Generated by Code IQ