Flow maybe types accept both null and undefined. When the final
parameter of a function accepts a maybe type, passing nothing
(undefined) successfully typechecks, which can lead to bugs if the
omission is accidental. Being forced to pass null is harder to screw up.
Explicit null checks are also faster.
* 'beginLifeCycleTimer' function of ReactDebugTool.js
* 'bindAutoBindMethod' function of ReactClass.js
* 'warnNoop' function of ReactServerUpdateQueue.js
* 'getInternalInstanceReadyForUpdate' function of ReactUpdateQueue.js
* 'warnNoop' function of ReactNoopUpdateQueue.js
* 'getDeclarationErrorAddendum' function of ReactDOMComponent.js
* 'getSourceInfoErrorAddendum' function of ReactElementValidator.js
* 'getDeclarationErrorAddendum' function of instantiateReactComponent.js and ReactElementValidator.js
* 'traverseAllChildrenImpl' function of traverseAllChildren.js
* 'attachRef' function of ReactRef.js
* 'mountIndeterminateComponent' function of ReactFiberBeginWork.js
* 'createFiberFromElementType' function of ReactFiber.js
* 'getDeclarationErrorAddendum' function of ReactDOMSelect.js
* 'unmountComponentAtNode' function of ReactMount.js
* 'getDeclarationErrorAddendum' function of ReactControlledValuePropTypes.js
* 'checkRenderMessage' function of CSSPropertyOperations.js
* 'getDeclarationErrorAddendum' function of ReactDomFiberSelect.js
* 'getCurrentComponentErrorInfo' function in 'ReactElementValidator'
* 'getDeclarationErrorAddendum' function in ReactDOMFiberComponent.js
It is slightly more useful this way because when we want to find host nodes
we typically want to do so in the second phase. That's the real tree where
as the first phase is more of a virtual part of the tree.
* Allow renderers to return an update payload in prepareUpdate
This then gets stored on updateQueue so that the renderer doesn't need to
think about how to store this.
It then gets passed into commitUpdate during the commit phase.
This allows renderers to do the diffing during the time sliced path,
allocate a queue for changes and do only the absolute minimal work to
apply those changes in the commit phase.
If refs update we still schedule and update.
* Hack around the listener problem
* Diff ReactDOMFiber properties in prepareUpdate
We now take advantage of the new capability to diff properties early.
We do this by generating an update payload in the form of an array with
each property name and value that we're about to update.
* Add todo for handling wasCustomComponentTag
* Always force an update to wrapper components
Wrapper components have custom logic that gets applied at the commit phase
so we always need to ensure that we schedule an update for them.
* Remove rootContainerInstance from commitMount
No use case yet and I removed it from commitUpdate earlier.
* Use update signal object in test renderer
* Incorporate 8652 into new algorithm
* Fix comment
* Add failing test for flipping event handlers
This illustrates the problem that happens if we store a pointer to the
Fiber and then choose not to update that pointer when no properties change.
That causes an old Fiber to be retained on the DOM node. Then that Fiber
can be reused by the pooling mechanism which then will mutate that Fiber
with new event handlers, which makes them active before commit.
* Store current props in the RN instance cache and on the DOM node
This represents the current set of event listeners. By not relying on the
Fiber, it allows us to avoid doing any effects in the commit phase when
nothing changes.
This is a bit ugly. Not super happy how this all came together.
Currently we update the memoized inputs (props, state) during the
complete phase, as we go back up the tree. That means we can't reuse
work until of its children have completed.
By moving memoization to the begin phase, we can do a shallow bailout,
reusing a unit of work even if there's still work to do in its children.
Memoization now happens whenever a fiber's `child` property is updated;
typically, right after reconciling. It's also updated when
`shouldComponentUpdate` returns false, because that indicates that the
given state and props are equal to the memoized state and props.
* ReactTestRenderer move current impl to stack dir
* ReactTestRenderer on fiber: commence!
* ReactTestRenderer: most non-ref/non-public-instance tests are passing
* Move ReactTestFiberComponent functions from Renderer to Component file
* test renderer: get rid of private root containers and root Maps
* TestRenderer: switch impl based on ReactDOMFeatureFlag.useFiber
* ReactTestRenderer: inline component creation
* ReactTestRenderer: return to pristine original glory (+ Fiber for error order difference)
* TestRendererFiber: use a simple class as TestComponentInstances
* Add `getPublicInstance` to support TestRenderer `createNodeMock`
* Rename files to end. Update for `mountContainer->createContainer` change
* test renderer return same object to prevent unnecessary context pushing/popping
* Fiber HostConfig add getPublicInstance. This should be the identity fn everywhere except the test renderer
* appease flow
* Initial cleanup from sleepy work
* unstable_batchedUpdates
* Stack test renderer: cache nodeMock to not call on unmount
* add public instance type parameter to the reconciler
* test renderer: set _nodeMock when mounted
* More cleanup
* Add test cases for root fragments and (maybe?) root text nodes
* Fix the npm package build
Explicitly require the Stack version by default.
Add a separate entry point for Fiber.
We don't add fiber.js to the package yet since it's considered internal until React 16.
* Relax the ref type from Object to mixed
This seems like the most straightforward way to support getPublicInstance for test renderer.
* Remove accidental newline
* test renderer: unify TestComponent and TestContainer, handle root updates
* Remove string/number serialization attempts since Fiber ensures all textInstances are strings
* Return full fragments in toJSON
* Test Renderer remove TestComponent instances for simple objects
* Update babylon for exact object type syntax
* Use $$typeof because clarity > punching ducks.
* Minor Flow annotation tweaks
* Tweak style, types, and naming
* Fix typo
* Verify that functional component ref warning is deduplicated
It's not a big problem for string refs because the ref stays the same and the warning code path runs once per mount.
However, it is super annoying for functional refs since they're often written as arrows, and thus the warning fires for every render.
Both tests are currently failing since we're mounting twice, so even the string ref case prints warnings twice.
* Extract the warning condition to the top level
We don't want to run getStackAddendumByID() unless we actually know we're going to print the warning.
This doesn't affect correctness. Just a performance fix.
* Deduplicate warnings about refs on functional components
This fixes the duplicate warnings and adds an additional test for corner cases.
Our goal is to print one warning per one offending call site, when possible.
We try to use the element source for deduplication first because it gives us the exact JSX call site location.
If the element source is not available, we try to use the owner name for deduplication.
If even owner name is unavailable, we try to use the functional component unique identifier for deduplication so that at least the warning is seen once per mounted component.
* Fiber: warn for refs on SFCs
* Stateless refs: update warning to use component stack
* Warn for function refs (stack and fiber)
* Add owner reference to ref warnings
* Centralize stack ref warnings in ReactRef.attachRef
* Fiber stateless comp ref warning should only do work when necessary
* ReactDebugCurrentFiber maybe FunctionalComponents should act this way instead
* (chore) scripts/fiber/record-tests
* Add component._compositeType to ReactInstance Flow definition
* Don't handle 'stack inside fiber' case in the warning
We don't have a test for it. It's easy to mess it up and print the wrong thing so instead of verifying it I'll just remove this bit.
* Revert the change to getCurrentFiberOwnerName
This happened to work, but it is just a coincidence. This change didn’t really match what the function was supposed to be doing.
I’m not sure what the correct fix would be yet so this commit breaks tests.
* Add component indirection to the tests using owner name
This passes in Stack. It helps ensure we supply the correct owner name.
* Invalid type invariant should read owner from element
This brings the Fiber behavior in line with how Stack does it. The Fiber test was passing accidentally but broke down in more complicated cases (such as when we have an <Indirection> between the owner and the failing element).
Now we can also remove the weird cases from getCurrentFiberOwnerName() that didn't really make sense but helped get the (incomplete) tests pass in the past.
* Fiber should throw on a string ref inside functional component
The DOM renderer assumes that resetAfterCommit is called after
prepareForCommit without any nested commits in between. That may not
be the case now that syncUpdates forces a nested update.
To address, this changes the type of prepareForCommit to return a value
which is later passed to resetAfterCommit.
* Push class context providers early
Previously we used to push them only after the instance was available. This caused issues in cases an error is thrown during componentWillMount().
In that case we never got to pushing the provider in the begin phase, but in complete phase the provider check returned true since the instance existed by that point. As a result we got mismatching context pops.
We solve the issue by making the context check independent of whether the instance actually exists. Instead we're checking the type itself.
This lets us push class context early. However there's another problem: we might not know the context value. If the instance is not yet created, we can't call getChildContext on it.
To fix this, we are introducing a way to replace current value on the stack, and a way to read the previous value. This also helps remove some branching and split the memoized from invalidated code paths.
* Add a now-passing test from #8604
Also rename another test to have a shorter name.
* Move isContextProvider() checks into push() and pop()
All uses of push() and pop() are guarded by it anyway.
This makes it more similar to how we use host context.
There is only one other place where isContextProvider() is used and that's legacy code needed for renderSubtree().
* Clarify why we read the previous context
* Use invariant instead of throwing an error
* Fix off-by-one in ReactFiberStack
* Manually keep track of the last parent context
The previous algorithm was flawed and worked by accident, as shown by the failing tests after an off-by-one was fixed.
The implementation of getPrevious() was incorrect because the context stack currently has no notion of a previous value per cursor.
Instead, we are caching the previous value directly in the ReactFiberContext in a local variable.
Additionally, we are using push() and pop() instead of adding a new replace() method.
ReactFiberStack is the new underlying stack used by ReactFiberContext and ReactFiberHostContext. The goal is to simplify the 2 context managers and to add more errors/dev-warnings when we pop unexpected.
This changeset currently causes a lot of tests to fail (as we are currently popping too many times and/or in the wrong order). I'm pushing this branch now to share with Sebastian as he is working on a related cleanup pass at beginWork.
This was a bug because of the split between ClassComponent and IndeterminateComponent -- we didn't mark effects or push context when we come from an indeterminate component. Now they behave the same way.
The code is even clumsier than before but I'm pretty worried we'll screw these up in the future if we don't unify the paths.
Not many components exercise this path but Relay containers do so it's noticeable.
This is the same thing as the previous commit but with class, root and portal.
I noticed host and portal now already covers this case in their branches
since pushHostContainer is always at the top.
Since we only call bailout from within the host component branch now, we
can just move this to the branch where we already know that this is a
host component.
As part of this I noticed that we don't always push the host context so
I moved that to the beginning of the branch.
This is no longer needed. It is effectively covered by other branches
that tries to being merging the update queue which yields the same state
object as before if there is no work. That in turn bails out.
We can shortcut in the relevant paths if needed.
Now we only bail out once we've entered each branch.
This has some repetition with regard to hasContextChanged but I'm hoping
we can just get rid of all those branches at some point since they're
mostly for backwards compatibility right now.
The functional component branch is a bit special since it has sCU in it.
The class component branch actually already covers this in a bit convoluted
way.
I will next replicate this check in each other branch.
This consolidates the reset of context to one single place right before
any new work is started.
This place is burried in a bit of an awkward place but findNextUnitOfWork
happens right before any new work starts. That way we're guaranteed to have
an empty stack before we start anything new.
The benefit of this is that we don't have to rely .return being null when
entering beginWork but we also don't have to check it every time in the
hot path.
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.
We need to be able to access both, and since the list uses forward
pointers, it makes more sense to point to the one that comes first.
Otherwise to get the last progressed update you have to start at the
beginning of the list.
When resetting the priority in the complete phase, check the priority of
the update queue so that updates aren't dropped.
Updates inside render, child cWRP, etc are no longer dropped.
The next step is sort the queue by priority and only flush updates that
match the current priority level.
Restructures the update queue to maintain a pointer to the first
pending update, which solves a few problems:
- Updates that occur during the begin phase (e.g. in cWRP of a child)
aren't dropped, like they are currently. This isn't working yet because
the work priority is reset during completion. The following item will
fix it.
- Sets us up to be able to add separate priorities to each update in
the queue. I'll add this in a subsequent commit.
* Implement component stack for some warnings in Fiber
* Keep Fiber debug source up to date
When an element changes, we should copy the source and owner again.
Otherwise they can get stale since we're not reading them from the element.
* Remove outdated TODOs from tests
* Explicitly specify Fiber types to include in the stack
Fixes an accidental omission when both source and owner are null but displayName exists.
* Fix mised Stack+Fiber test to not expect extra warnings
When we're in Fiber mode we don't actually expect that warning being printed.
* Warn on passing different props to super()
* Implement duplicate key warnings
We keep known keys in a set in development. There is an annoying special case where we know we'll check it again because we break out of the loop early.
One test in the tree hook regresses to the failing state because it checks that the tree hook works without a Set available, but we started using Set in this code. It is not essential and we can clean this up later when we decide how to deal with polyfills.
* Move ReactTypeOfWork to src/shared
It needs to be available both to Fiber and Isomorphic because the tree hook lives in Isomorphic but pretty-prints Fiber stack.
* Add dev-only ReactDebugCurrentFiber for warnings
The goal is to use ReactCurrentOwner less and rely on ReactDebugCurrentFiber for warning owner name and stack.
* Make Stack invariant messages more consistent
Fiber used a helper so two tests had the same phrasing.
Stack also used a helper for most invariants but hardcoded a different phrase in one place.
I changed that invariant message to use a helper which made it consistent with what it prints in Fiber.
* Make CSSPropertyOperations use getCurrentFiberOwnerName()
This gets mount-time CSS warnings to be printed.
However update-time warnings are currently ignored because current fiber is not yet available during the commit phase.
We also regress on HostOperation hook tests but this doesn't matter because it's only used by ReactPerf and it doesn't work with Fiber yet anyway. We'll have to think more about it later.
* Set ReactDebugCurrentFiber during the commit phase
This makes it available during updates, fixing the last failing test in CSSPropertyOperations.
* Add DOM warnings by calling hooks directly
It is not clear if the old hook system is worth it in its generic incarnation. For now I am just hooking it up to the DOMFiber renderer directly.
* Add client-side counterparts for some warning tests
This helps us track which warnings are really failing in Fiber, and which ones depend on SSR.
* Test that SVG elements get created with the right namespace
* Pass root to the renderer methods
* Keep track of host instances and containers
* Keep instances instead of fibers on the stack
* Create text instances in begin phase
* Create instance before bailing on offscreen children
Otherwise, the parent gets skipped next time.
We could probably create it later but this seems simpler.
* Tweak magic numbers in incremental tests
I don't understand why they changed but probably related to us moving some work into begin phase?
* Only push newly created nodes on the parent stack
Previously I was pushing nodes on the parent stack regardless of whether they were already in current or not.
As a result, insertions during updates were duplicated, and nodes were added to existing parents before commit phase.
Luckily we have a test that caught that.
* Fix lint
* Fix Flow
I had to wrap HostContext API into a closure so that it's parameterizeable with I and C.
* Use the same destructuring style in scheduler as everywhere else
* Remove branches that don't seem to run anymore
I'm not 100% sure this is right but I can't get tests to fail.
* Be explicit about the difference between type and tag
I was confused by th HACK comment so I learned how DOM and SVG work with casing and tried to write a more descriptive comment.
It also seems like passing fiber.type into finalizeInitialChildren() is a potential problem because DOM code assumes tag is lowercase.
So I added a similar "hack" to finalizeInitialChildren() that is identical to the one we have prepareUpdate() so if we fix them later, we fix both.
* Save and restore host context when pushing and popping portals
* Revert parent context and adding children in the begin phase
We can address this later separately as it is a more hot path.
This doesn't affect correctness of SVG container behavior.
* Add a test for SVG updates
This tests the "jump" reuse code path in particular.
* Record tests
* Read ownerDocument from the root container instance
This way createInstance() depends on the innermost container only for reading the namespace.
* Track namespaces instead of creating instances early
While we might want to create instance in the begin phase, we shouldn't let DOM guide reconciler design.
Instead, we are adding a new concept of "host context". In case of ReactDOMFiber, it's just the current namespace.
We are keeping a stack of host context values, ignoring those that are referentially equal.
The renderer receives the parent context and type, and can return a new context.
* Pop child context before reading own context and clarify API
It wasn't quite clear from the API which context was being returned by the renderer. Changed the API to specifically ask for child context, and thus to pop it before getting the current context.
This fixes the case with <foreignObject> to which I intended to give SVG namespace.
* Give SVG namespace to <svg> itself
* Don't allocate unnecessarily when reconciling portals
We create stacks lazily so that if portal doesn't contain <svg>s, we don't need to allocate.
We also reuse the same object for portal host context state instead of creating a new one every time.
* Add more tests for edge cases
* Fix up math namespace
* Maintain a separate container stack
* Fix rebase mistakes
* Unwind context on errors
* Reset the container state when reusing the object
* Add getChildHostContext() to ReactART
* Record tests
This uncovered a separate issue where some errors were being unscheduled
and rescheduled multiple times before flushing. Turns out we need to
check both a fiber and its alternate when determining if it represents a
failed unit of work. I didn't notice this before because we were
scheduling a new update on *every* boundary at the end of the commit
phase, not just the ones that captured an error during that commit. I
updated the unit tests to catch this in the future.
Fixes a bug when updating from a single text child (or
dangerouslySetInnerHTML) to regular children, where the previous
text content never gets deleted.
* Exit early in scheduleUpdate if a node's priority matches
This is a performance optimization and is unobservable. However, it
helps protect against regressions on the following invariants on which
it relies:
- The priority of a fiber is greater than or equal to the priority of
all its descendent fibers.
- If a tree has pending work priority, its root is scheduled.
* New error boundary semantics
- Recovering from an error boundary no longer uses Task priority by
default. The work is scheduled using whatever priority created the
error.
- Error handling is now a side-effect that happens during the
commit phase.
- The default behavior of an error boundary is to render null. Errors
do not propagate except when an boundary fails. Conceptually, this would
be like throwing an error from a catch block.
- A host container is treated like a no-op error boundary. The first
error captured by a host container is thrown at the end of the batch.
Like with normal error boundaries, the entire tree is unmounted.
* Fix broken setState callback test
* Add test for "unwinding" context when an error interrupts rendering
* Switch over primary effect types only
This avoids the need to create an export for every combination of bits.
* Only continue the work loop if the error was successfully captured
* Add more tests for incremental error handling
These tests are currently failing:
✕ catches render error in a boundary during partial deferred mounting
✕ catches render error in a boundary during animation mounting
✕ propagates an error from a noop error boundary during full deferred mounting
✕ propagates an error from a noop error boundary during partial deferred mounting
✕ propagates an error from a noop error boundary during animation mounting
The observed behavior is that unstable_handleError() unexpected gets called twice:
"ErrorBoundary render success",
"BrokenRender",
"ErrorBoundary unstable_handleError",
+ "ErrorBoundary render success",
+ "BrokenRender",
+ "ErrorBoundary unstable_handleError",
"ErrorBoundary render error"
* Verify batched updates get scheduled despite errors
* Add try/catch/finally blocks around commit phase passes
We'll consolidate all these blocks in a future PR that refactors the
commit phase to be separate from the perform work loop.
* NoopBoundary -> RethrowBoundary
* Only throw uncaught error once there is no more work to perform
* Remove outdated comment
It was fixed in #8451.
* Record tests
* Always reset nextUnitOfWork on error
This is important so that the test at the end of performAndHandleErrors() knows it's safe to rethrow.
* Add a passing test for unmounting behavior on crashed tree
* Top-level errors
An error thrown from a host container should be "captured" by the host
container itself
* Remove outdated comment
* Separate Rethrow and Noop scenarios in boundary tests
* Move try block outside the commit loops