-
Notifications
You must be signed in to change notification settings - Fork 47.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Schedule state and callback at the same time #8585
Conversation
d99319c
to
9f68a27
Compare
const isTopLevelUnmount = Boolean( | ||
partialState && | ||
partialState.element === null | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At a glance, this change seems unrelated to the core of this PR, and also like the opposite of what the logic used to be. Was it a bug before? I don't see any other change inside of this PR that looks as though it would have invalidated the previous check, but maybe I'm missing it.
eg I would expect to see a change more like
const isTopLevelUnmount = (
partialState === null ||
partialState.element === null
);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
partialState
at the top level is always an object with the shape { element: something }
. The previous test was just wrong. I thought about writing a unit test for this but it's unobservable except in incremental mode. And when we do switch to incremental by default the top-level mount/update/unmount API will have to change anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your suggested change is wrong because
Object.assign({}, { element: element }, null);
merely copies the previous state, whereas an unmount sets element
to null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The previous test was just wrong.
Gotcha. This is what I thought may be the case but I wasn't sure.
} else { | ||
internalInstance._pendingCallbacks = [callback]; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we manually inline these 3 blocks (and the one in enqueueCallback
) for performance reasons?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't seem worth extracting into a function.
); | ||
|
||
if (ReactDOMFeatureFlags.useFiber) { | ||
expected.push(['initial-callback', 'yellow']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happened here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explained by the now-deleted comment above:
In Fiber, the initial callback is not enqueued until after any work scheduled by lifecycles has flushed (same semantics as a regular setState callback outside of a batch). In Stack, the initial render is scheduled inside of batchedUpdates, so the callback gets flushed right after componentDidMount.
TODO: We should fix this, in both Stack and Fiber, so that the behavior is consistent regardless of whether you're in a batch.
'Inner-setState-1', | ||
'Inner-render-1-1', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did this rerender immediately before? I thought this change would only move some setState callbacks earlier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm actually not sure why it re-rendered immediately before, since setState
is called inside componentDidUpdate
which should happen in a batch regardless. Is this too significant a change to make in a non-major release?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should figure out why there was a change before we can make a meaningful decision on that point. We've made other breaking changes for Fiber so we'll need to do a 16 regardless before releasing Fiber (or maybe at the same time).
const fiber = ReactInstanceMap.get(instance); | ||
scheduleSetState(fiber, partialState); | ||
const priorityLevel = getPriorityContext(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you choose to split these out rather than adding callback
as an argument to scheduleSetState
etc? This feels like it's asking for someone to accidentally introduce the problem this PR is fixing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was going to remove scheduleSetState
et al from the scheduler anyway because they were just an extra layer of indirection around addUpdate
et al. See @sebmarkbage's comment here for context: #8538 (comment)
To avoid introducing this problem again we could delete addCallback
and enqueueCallback
since they're no longer used. Sebastian thought it was better not to break the existing class updater API on the off chance someone was relying on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather remove them. I don't think we've ever described these as public at all. Google doesn't show anyone talking about them. I'll leave it up to you though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok then I'll remove
const fiber = ReactInstanceMap.get(instance); | ||
scheduleSetState(fiber, partialState); | ||
const priorityLevel = getPriorityContext(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather remove them. I don't think we've ever described these as public at all. Google doesn't show anyone talking about them. I'll leave it up to you though.
Fixes an issue (in both Stack and Fiber) where enqueueSetState causes a synchronous update that flushes before enqueueCallback is ever called. Now enqueueSetState et al accept an optional callback so that both are scheduled at the same time.
This isn't being used anymore. Also removes addCallback from ReactFiberUpdateQueue.
9f68a27
to
f4c0c99
Compare
Fixes an issue (in both Stack and Fiber) where
enqueueSetState
causes a synchronous update that flushes beforeenqueueCallback
is ever called. NowenqueueSetState
et al accept an optional callback so that both are scheduled at the same time.