react-ionize
react-ionize copied to clipboard
Fiber renderer notes
Nice reverse engineering work. (Sorry nothing is documented yet.)
A few notes on your custom renderer implementation:
// These functions have something to do with how updates are prioritized and
// scheduled. I have NO idea how the 'timeRemaining' piece works, but I've
// pretty much lifted it wholesale from ReactTestRendererFiber and it seems to
// work OK.
Take a look at requestIdleCallback doc which scheduleDeferredCallback() was modeled after. The idea is that timeRemaining() is provided by the browser (or other host environment). We do a little bit of work and then check timeRemaining(). If it gets below zero then it’s time for us to stop doing work. @linclark’s talk on Fiber goes into more details, but the idea is that we let the host environment notify us about when idle periods start and end, and we squeeze some work in that period. We only do this for low priority updates (which can “wait” and don’t happen synchronously). Which brings me to...
// For these last two, I got nothin'. That's why they're at the bottom.
export const useSyncScheduling = false;
This actually opts you into using low priority updates for setState by default. Which currently has bugs and starvation issues (some updates may never flush if they arrive too fast, and we haven’t solved this yet). So you might want to use true for now if you care about correctness. But false is more fun for experimenting with how Fiber schedules work. ReactDOM 16 has this set to true for backwards compatibility, and we’ll look into making it false by default in 17 when we figure out all the edge cases and bugs.
export function shouldDeprioritizeSubtree(
This is an interesting one. If you return true from it for some props, it will treat this tree as unimportant (but still existing)—unimportant enough to not show it. Think something like display:none in ReactDOM. You know this thing isn’t displayed, and this means React can skip this work on initial render. However if it’s idle, it might as well start rendering it piece by piece. This is what happens for deprioritized subtrees: if you return true, you’re saying it’s safe to completely skip this subtree, but it would be nice to start rendering it in background.
Of course, this relies on a high fidelity scheduleDeferredCallback() implementation. If it’s just a setTimeout() then it’s going to be a really bad one because instead of being called in idle time, it will clobber up your CPU. So if you don’t have a good scheduleDeferredCallback() implementation, it’s better not to enable shouldDeprioritizeSubtree(), and it’s best to stick with useSyncScheduling.
Let me know if you have more questions!
Also if you have feedback on the custom renderer API. We can change it if some methods are named confusingly. Just send a PR and let’s talk.
@gaearon weird place maybe to ask but have a question...
I have an update to ionize using 16.1 working but am seeing behavior I saw in ionize as well, so more confident it's something I'm misunderstanding vs just a bug in renderer.
There's a pattern like this:
this.state.show && <Window onClose={() => this.setState({ show: false })} />
Where onClose side effect basically removes said window. What I see is that removeChild seems to happen, but then afterwards another commitUpdate comes through.
In my removeChild I tried doing child.unmounted = true and then checking that in the commitUpdate, but strangely, it doesn't seem to persist that mutation, or I'm just missing something here.
Does this ring any bell? I can push this up into a repo but I also see it on ionize itself.
A minimal reproducing case would be helpful. Hard to say otherwise.