* [Work-in-progress] Assign expiration times to updates
An expiration time represents a time in the future by which an update
should flush. The priority of the update is related to the difference
between the current clock time and the expiration time. This has the
effect of increasing the priority of updates as time progresses, to
prevent starvation.
This lays the initial groundwork for expiration times without changing
any behavior. Future commits will replace work priority with
expiration times.
* Replace pendingWorkPriority with expiration times
Instead of a priority, a fiber has an expiration time that represents
a point in the future by which it should render.
Pending updates still have priorities so that they can be coalesced.
We use a host config method to read the current time. This commit
implements everything except that method, which currently returns a
constant value. So this just proves that expiration times work the same
as priorities when time is frozen. Subsequent commits will show the
effect of advancing time.
* Triangle Demo should use a class
shouldComponentUpdate was removed from functional components.
Running the demo shows, now that expiration is enabled, the demo does
not starve. (Still won't run smoothly until we add back the ability to
resume interrupted work.)
* Use a magic value for task expiration time
There are a few cases related to sync mode where we need to distinguish
between work that is scheduled as task and work that is treated like
task because it expires. For example, batchedUpdates. We don't want to
perform any work until the end of the batch, regardless of how much
time has elapsed.
* Use current time to calculate expiration time
* Add unit tests for expiration and coalescing
* Delete unnecessary abstraction
* Move performance.now polyfill to ReactDOMFrameScheduling
* Add expiration to fuzz tester
* Expiration nits
- Rename Done -> NoWork
- Use max int32 instead of max safe int
- Use bitwise operations instead of Math functions
* Add a failing test that prints "null" in the warning instead of the stack
* Don't reset the current fiber during reconciliation
It should only be reset when we stop doing work.
Otherwise, any warnings after the reset will lose the component stack.
The reset in getMaskedContext() was completely unnecessary.
It is always called with the same fiber as the current work in progress.
Therefore, I add a DEV-only warning assertion to ensure we don't regress, and remove the reset.
The reset in processChildContext() is necessary because it can be called outside of reconciliation.
Unfortunately, we have to keep this hack in until we can remove unstable_renderSubtreeIntoContainer().
To work around it, I restore the previous fiber instead of resetting.
* Decouple setting the current fiber and phase
These are two distinct actions.
This helps make it clearer when we're actually changing the current pointer.
I'm also removing an overengineered hack I previously added for unstable_renderSubtreeIntoContainer. It's not necessary now that we don't null the pointer all the time.
This makes the code more straightforward.
* Centralize the pointer updates in the scheduler
Did find and replace in TextMate.
```
find: (?:( \*)( ))?Copyright (?:\(c\) )?(\d{4})\b.+Facebook[\s\S]+(?:this source tree|the same directory)\.$
replace: $1$2Copyright (c) $3-present, Facebook, Inc.\n$1\n$1$2This source code is licensed under the MIT license found in the\n$1$2LICENSE file in the root directory of this source tree.
```
* Extract the top element frame from ReactDebugCurrentFrame
This is part of a larger refactor to decouple stack addendums. All
renderers have their own way of getting the stack of the currently
executing components.
There is one special case in Element Validator that adds an additional line
for the element being validated. This commit moves that special case in
into the validator.
There is another case where it looked like this was used in shallow
renderer but this is actually something different. It is part of the
component stack. It just happens to be that shallow renderer has a simpler
implementation of the component stack that just happens to be a single
element.
This will let us decouple the implementation to get a stack from
ReactDebugCurrentFrame and put that in each renderer.
* Stop using ReactComponentTreeHook for Fiber
Currently we fall back to ReactCurrentOwner in ReactComponentTreeHook for
stack addendums. We shouldn't need to because we should use
ReactDebugCurrrentFiber.
Ensure we always set both ReactDebugCurrentFiber and ReactDebugCurrentFrame
so that we can rely on these for all stacks.
* Make ReactDebugCurrentFrame implementation independent
Introduced ReactDebugCurrentStack for the Stack renderer which does the
same thing as ReactDebugCurrentFiber.
ReactDebugCurrentFrame no longer keeps track of the current fiber/debug id.
That's handled by the individual renderers.
Instead, it is now used to keep track of the current *implementation* of
the current stack frame. That way it is decoupled from the specifics of
the renderers. There can be multiple renderers in a context. What matters
is which one is currently executing a debuggable context (such as a render
function).
* Add debug frames to ReactPartialRenderer (ssr)
Basic functionality.
* Add shared modules to shallow renderer
This is now needed because we share describeComponentFrame.
To make sure we don't reset the priority of a down-prioritized fiber,
we compare the priority we're currently rendering at to the fiber's
work priority. If the work priority is lower, then we know not to reset
the work priority.
The current implementation of resuming work is buggy. The underlying
model is also flawed. Rather than attempt to fix a flawed model, we'll
scrap the feature entirely and add it back later.
* Don't hydrate any properties other than event listeners and text content
This strategy assumes that the rendered HTML is correct if the tree lines
up. Therefore we don't diff any attributes of the rendered HTML.
However, as a precaution I ensure that textContent *is* updated. This
ensures that if something goes wrong with keys lining up etc. at least
there is some feedback that the event handlers might not line up. With
what you expect. This might not be what you want e.g. for date formatting
where it can different between server and client.
It is expected that content will line up. To ensure that I will in a follow
up ensure that the warning is issued if it doesn't line up so that in
development this can be addressed.
The text content updates are now moved to the commit phase so if the tree
is asynchronously hydrated it doesn't start partially swapping out. I use
the regular update side-effect with payload if the content doesn't match up.
Since we no longer guarantee that attributes is correct I changed the
bad mark up SSR integration tests to only assert on the textContent
instead.
* Hydrate text node if possible
Currently we're never matching text nodes so we need to properly branch.
* bump flow to 0.47
* Fix variadic function flow issues in fiber
* Fix variadic function flow issues in ReactFiberNative
* fix ReactDOM type issues with flow 0.47
* getChildHostContext *does* take an `instance` argument
* change recently added anys to mixedies
* HydrationContext needs a handle on the rootContainerInstance
* prettier
* Don't double validate the DOM container
The warnings need to check a valid container but that should happen in
unstable_renderSubtreeIntoContainer too so might as well move it in.
* Hydrating DOM
Hydrates a server-rendered DOM tree by traversing it and connecting up the nodes if they match. Attributes are not diffed. They're assumed to be accurate on matching nodes.
* Remove meta data filtering from test
Because the current server renderer includes the meta data, it remains in a revived tree.
* Annotate because Flow
* Don't track insertion effects if we're going to hydrate
During initial mount, we should not track Placement
* Fix up test cases to ignore errors that we no longer throw
TODO make these warnings instead.
* Correctly track hydration state inside a newly inserted tree
When we don't match the first hydration node, we'll do an insertion.
Currently we keep the next hydratable sibling so that we know where to
pick up once we're done with the insertion. Unfortunately this makes the
nodes inside the insertion think that there's a node to hydrate.
I used to check for the direct parent but that doesn't work on nested host components.
We need to instead keep track of that we're in an hydration context but
we're not currently hydrating. Once we pop passed the inserted node can
we resume hydrating.
* Hacky fix to isMounted
isMounted checks whether a component is inside a Placement. During
hydration we ideally don't do any Placements if hydration matches the tree.
To work around this I use the Placement flag on the root, which isn't used
for anything else. But only temporarily while we're hydrating. Then reset
it before committing.
* Record tests
* Comments
* Split ReactNativeFiber into separate ReactNativeFiberRenderer module
Hopefully this is sufficient to work around Rollup circular dependency problems. (To be seen in subsequent commits...)
* Split findNodeHandle into findNodeHandleFiber + findNodeHandleStack
This allowed me to remove the ReactNative -> findNodeHandle injections, which should in turn allow me to require a fully-functional findNodeHandle without going through ReactNative. This will hopefully allow ReactNativeBaseomponent to avoid a circular dependency.
* Un-forked findNodeHandle in favor of just inlining the findNode function impl
* takeSnapshot no longer requires/depends-on ReactNative for findNodeHandle
Instead it uses the new, renderer-specific wrappers (eg findNodeHandleFiberWrapper and findNodeHandleStackWrapper) to ensure the returned value is numeric (or null). This avoids a circular dependency that would trip up Rollup.
* NativeMethodsMixin requires findNodeHandler wrapper(s) directly rather than ReactNative
This works around a potential circular dependency that would break the Rollup build
* Add RN_* build targets to hash-finle-name check
* Strip @providesModule annotations from headers for RN_* builds
* Added process.env.REACT_NATIVE_USE_FIBER to ReactNativeFeatureFlags
This is kind of a hacky solution, but it is temporary. It works around the fact that ReactNativeFeatureFlag values need to be set at build time in order to avoid a mismatch between runtime flag values. DOM avoids the need to do this by using injection but Native is not able to use this same approach due to circular dependency issues.
* Moved a couple of SECRET exports to dev-only. Removed SyntheticEvent and PooledClass from SECRET exports. Converted Rollup helper function to use named params.
* Split NativeMethodsMixins interface and object-type
* Add @noflow header to flat-bundle template to avoid triggering Flow problems
When Flow tries to infer such a large file, it consumes massive amounts of CPU/RAM and can often lead to programs crashing. It is better for such large files to use .flow.js types instead.
* NativeMethodsMixin and ReactNativeFiberHostComponent now share the same Flow type
* Collocated (externally exposed) ReactTypes and ReactNativeTypes into single files to be synced to fbsource. ReactNativeFiber and ReactNativeStack use ReactNativeType Flow type
* Build script syncs RN types and PooledClass automatically
* Added optional sync-RN step to Rollup build script
* Added results.json for new RN bundles
* Don't schedule class fibers without relevant lifecycles for commit
* Separate Update and Ref effects
* Simplify the exit condition
* Add missing annotation
* Write conditions differently to avoid an extra check
* Inline markUpdateIfNecessary()
* Inline markUpdateIfAlreadyInProgress()
* Add forwarding modules
* Codemod to use full package path outside of own package
Files that require modules from a different package than their own now
does so by the npm path name instead of the providesModule.
* Codemod fbjs module dependencies
* Fix gulp module mapping config
This is a bit lame but because of our module rewrite we need to white
list all the paths that we don't *don't* want to rewrite.
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.
When visiting the yields, the root is the stateNode of the coroutine. If
its return value is wrong we'll end up at the alternate of the
workInProgress which will start scanning its children which is wrong.
Coroutines was kind of broken because it tried to do reparenting and
enabling state preservation to be passed along the coroutine. However,
since we couldn't determine which Fiber was "current" on a reified yield
this was kind of broken.
This removes the "continuation" part of yields so they're basically just
return values. It is still possible to do continuations by just passing
simple functions or classes as part of the return value but they're not
stateful.
This means that we won't have reparenting, but I actually don't think we
need it. There's another way to structure this by doing all the state in
the first phase and then yielding a stateless representation of the result.
This stateless representation of the tree can then be rendered in different
(or even multiple) locations.
Because we no longer have a stateful continuation, you may have noticed
that this really no longer represent the "coroutine" concept. I will
rename it in a follow up commit.
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
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.
The finalizeInitialChildren HostConfig method now utilizes a boolean return type. Renderers can return true to indicate that custom effects should be processed at commit-time once host components have been mounted. This type of work is marked using the existing Update flag.
A new HostConfig method, commitMount, has been added as well for performing this type of work.
This change set is in support of the autoFocus prop.
* 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 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.
* (WIP) Nesting warnings
* Remove indirection
* Add a note about namespace
* Fix Flow and make host context required
This makes it easier to avoid accidental nulls.
I also added a test for the production case to avoid regressing because of __DEV__ branches.
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 handles the case where a host text bails out. In that case we need to
reuse its previous memoizedProps. We should also only schedule an actual
update if it did actually change its text content.
I updated the unit test to ignore comment nodes if we're using Fiber.
* Remove output field
The idea was originally that each fiber has a return value. In practice
most of what we're modelling here are void functions and we track side
effects instead of return values.
We do use this for coroutines to short cut access to terminal yields.
However, since this can be nested fragments we end up searching the tree
anyway. We also have to manage this in transferOutput so it ends up being
as expensive. Maybe we can save some traversal for updates when coroutine
branches bail out but meh.
* Unmount child from the first phase of a coroutine