Commit Graph

975 Commits

Author SHA1 Message Date
Sebastian Markbage bc5dfd5358 Break out some Class Component logic into separate module
Refactors the class logic a bit.

I moved scheduleUpdate out into the scheduler since that's where
the scheduling normally happens. I also moved it so that we can
rely on hoisting to resolve the cycle statically.

I moved the updater to a new class component file. I suspect we
will need a bit of space in here since the class initialization
code is quite complex.

The class component dependency is currently fixed in BeginWork
so we can't move complete or commit phase stuff to it. If we need
to, we have to initialize it in the scheduler and pass it to the
other phases.
2016-10-17 16:21:06 -04:00
Sebastian Markbage 9c25538e13 Fix resuming bug
If we abort work but have some completed, we can bail out if
the shouldComponentUpdate returns true. However, then we have
a tree that is low priority. When we bailout we currently use
the "current" tree in this case. I don't think this is right.
I'm not sure why I did that.

Similarly, when we complete we use the "current" props if we
didn't have pending props to do. However, we should be using
the memoized props of that tree if it is a pending work tree.

Added a unit test that covers these two cases.
2016-10-17 16:17:30 -04:00
Sebastian Markbage e59e5b280f Invoke all null ref calls before any new ref calls
This reorganizes the two commit passes so that all host
environment mutations happens before any life-cycles. That means
that the tree is guaranteed to be in a consistent state at that
point so that you can read layout etc.

This also lets us to detach all refs in the same pass so that
when they get invoked with new instances, that happens after it
has been reset.
2016-10-17 16:17:30 -04:00
Sebastian Markbage 3717b71c64 Resolve ref callbacks
During the deletion phase we call detachRefs on any deleted refs.

During the insertion/update phase we call attachRef on class
and host components.

Unfortunately, when a ref switches, we are supposed to call all
the unmounts before doing any mounts. This means that we have to
expact the deletion phase to also include updates in case they
need to detach their ref.
2016-10-17 16:17:30 -04:00
Sebastian Markbage c44c7eaa1b Fire componentDidMount/componentDidUpdate life-cycles
These happen in the commit phase *before* the setState callback.

Unfortunately, we've lost the previous state at this point since
we've already mutated the instance. This needs to be fixed
somehow.
2016-10-17 16:17:30 -04:00
Sebastian Markbage 90993c5324 Call componentWillUnmount during deletion phase
While we're deleting nodes, we need to call the unmount
life-cycle. However, there is a special case that we don't want
to keep deleting every host node along the way since deleting the
top node is enough.
2016-10-17 16:17:30 -04:00
Sebastian Markbage a98aba4d73 Always override priority level when visiting children
It is not possible for a child to have a higher priority level
than we're reconciling at, unless we intentionally want to
down-prioritize it.
2016-10-17 16:17:30 -04:00
Sebastian Markbage 43645a6586 Reset effect list when we recompute children
We only use the effect list when we reuse our progressed children.
Otherwise it needs to reset to null.

In all other branches, other than bailoutOnLowPriority, we
revisit the children which recreates this list.

We should also not add fibers to their own effect list since it
becomes difficult to perform work on self without touching the
children too. Nothing else does that neither.

Since that means that the root isn't added to an effect list we
need to special case the root.
2016-10-17 16:17:30 -04:00
Sebastian Markbage 1d437dc9ad Remove beginWork shortcut
This shortcut had a bug associated with it. If beginWork on this
child returns null, then we don't call completeWork on that fiber.

Since removing this short cut adds another time check, we have to
add a single unit of time in tests to account for the top level
call now taking one more unit.

This was also the only recursive call in all of fiber so it's nice
to get rid of it to guarantee that a flat stack is possible.
2016-10-17 16:17:30 -04:00
Sebastian Markbage 5de2821372 Move child updates to use the reconciled effects
Instead of passing the full list of children every time to
update the host environment, we'll only do inserts/deletes.

We loop over all the placement effects first and then later
we do the rest.
2016-10-17 16:17:30 -04:00
Sebastian Markbage 442ab71fc7 Append deletions to the effect list
First clear any progressed deletions for any case where we start
over with the "current" set of children.

Once we've performed a new reconciliation we need to add the
deletions to the side-effect list (which we know is empty because
we just emptied it).

For other effects, instead of just adding a fiber to an effect
list we need to mark it with an update. Then after completion
we add it to the the effect list if it had any effects at all.

This means that we lose the opportunity to control if a fiber
gets added before or after its children but that was already
flawed since we want certain side-effects to happen before others
on a global level.

Instead, we'll do multiple passes through the effect list.
2016-10-17 16:17:30 -04:00
Sebastian Markbage 901691eb92 Tag the fiber with the kind of side-effect that was applied to it
This allow us to track what kind of side-effect this was even
though we only have a single linked list for all side-effects.
2016-10-17 16:17:30 -04:00
Sebastian Markbage e9e645a9b1 Deletion tracking
When we reconcile children we need to track the deletions that
happen so that we can perf side-effects later as a result. The
data structure is a linked list where the next pointer uses the
nextEffect pointer.

However, we need to store this side-effect list for reuse if we
end up reusing the progressedChild set. That's why I add a
separate first/last pointer into this list so that we can keep it
for later.
2016-10-17 16:17:30 -04:00
Sebastian Markbage 0dc840a2b9 Fast path for create child
When we don't have any previous fibers we can short cut this path
knowing that we will never have any previous child to compare to.

This also ensures that we don't create an empty Map in this case.
2016-10-17 16:17:30 -04:00
Sebastian Markbage 3979bb9e24 Don't track side-effects unless needed
We don't need to track side-effects for a parent that has never
been mounted before. It will simply inject all its children when
it completes.
2016-10-17 16:17:30 -04:00
Sebastian Markbage cccc4ae2c6 Add index field to each fiber
We use this to track the index slot that this Fiber had at
reconciliation time. We will use this for two purposes:

1) We use it to quickly determine if a move is needed.

2) We also use it to model a sparce linked list, since we can have
null/false/undefined in our child set and these take up a slot for
the implicit key.
2016-10-17 16:17:30 -04:00
Sebastian Markbage edaf08fcfe Fiber child reconciliation
This implements the first step to proper child reconciliation.
It doesn't yet track side-effects like insert/move/delete but has
the main reconciliation algorithm in place.

The goal of this algorithm is to branch early and avoid rechecking those conditions. That leads to some duplications of code.

There are three major branches:

- Reconciling a single child per type.
- Reconciling all children that are in the same slot as before from the beginning.
- Adding remaining children to a temporary Map and reconciling them by scanning the map.

Even when we use the Map strategy we have to scan the linked list to line up "same slot" positions because React, unlike Inferno, can have implicit keys interleaved with explicit keys.
2016-10-17 16:17:30 -04:00
Sebastian Markbage 13f01be64f Enable text updates in ReactNoop
We'll enable updating of text nodes. To be able to test that we
need the text nodes to be mutable. They're currently just strings
in the Noop renderer so this makes them an object instead.

That exposed a bug in ReactFiberCommitWork for text nodes.
2016-10-17 16:17:30 -04:00
Sebastian Markbage 362e56a1c1 Add comment about bug in yields
This needs to be fixed somehow. The reconciler could know if you
are mounting this continuation into the same spot as before and
then clone it. However, if the continuation moves, this info is
lost. We'd try to unmount it in one place and mount the same fiber
in another place.
2016-10-17 16:17:30 -04:00
Sebastian Markbage c3310d3e32 Fix MultiChild tests so they work with Fiber
Dropped the unnecessary use of findDOMNode.
Dropped unnecessary arrow functions.
Math.random() -> id counter, since this used to be
non-deterministic which is not ideal for unit tests.

getOriginalKeys() used to rely on implementation details
to scan that the internal data structure maintained its
structure, however, since that is unobservable we don't
need to test the internal data structure itself. We're
already testing refs and dom structure which is the only
observable effect of the reorder.
2016-10-17 16:17:30 -04:00
Sebastian Markbage 8d3a58de62 Silence Fiber warning when the feature flag is on
When the feature flag is on, we should silence the warning since
we're explicitly testing it. This is needed when running unit
tests with the flag on.
2016-10-17 16:17:30 -04:00
Sebastian Markbage cafa7b284a Add Text node types
These nodes rendering into Text nodes in the DOM.

There is a special case when a string is a direct child of a host
node. In that case, we won't reconcile it as a child fiber. In
terms of fibers, it is terminal. However, the host config special
cases it.

It is kind of unfortunate that we have to special case this kind
of child in the HostConfig. It would be nice to unify this with
other types of child instances. Text nodes have some weird special
cases, but those special cases tend to *vary* by environment.
They're not the same special cases so not sure how valuable it is
to have a special protocol and special types for it.
2016-10-17 16:17:30 -04:00
Sebastian Markbage e0a305350b Add Fragment fiber type
We currently treat nested arrays as a unique namespace from top
level children. So that these won't share key namespaces. This
adds a new fiber type that will represent the position of a
fragment.

This is only used for nested arrays. Even if you return an array
from a composite component, we don't need this since they share
namespace with a single top level component.

This still doesn't implement the complete reconciliation
algorthim in child fiber. That's coming later.
2016-10-17 16:17:30 -04:00
Dan Abramov 7b7eddcc72 Add @preventMunge directives to classes (#7994) 2016-10-17 21:07:17 +01:00
Dan Abramov f33f03e357 Support passthrough updates for error boundaries (#7949)
* Initial pass at the easy case of updates (updates that start at the root).

* Don't expect an extra componentWillUnmount call

It was fixed in #6613.

* Remove duplicate expectations from the test

* Fix style issues

* Make naming consistent throughout the tests

* receiveComponent() does not accept safely argument

* Assert that lifecycle and refs fire for error message

* Add more tests for mounting

* Do not call componentWillMount twice on error boundary

* Document more of existing behavior in tests

* Do not call componentWillUnmount() when aborting mounting

Previously, we would call componentWillUnmount() safely on the tree whenever we abort mounting it. However this is likely risky because the tree was never mounted in the first place.

People shouldn't hold resources in componentWillMount() so it's safe to say that we can skip componentWillUnmount() if componentDidMount() was never called.

Here, we introduce a new flag. If we abort during mounting, we will not call componentWillUnmount(). However if we abort during an update, it is safe to call componentWillUnmount() because the previous tree has been mounted by now.

* Consistently display error messages in tests

* Add more logging to tests and remove redundant one

* Refactor tests

* Split complicated tests into smaller ones

* Assert clean unmounting

* Add assertions about update hooks

* Add more tests to document existing behavior and remove irrelevant details

* Verify we can recover from error state

* Fix lint

* Error in boundary’s componentWillMount should propagate up

This test is currently failing.

* Move calling componentWillMount() into mountComponent()

This removes the unnecessary non-recursive skipLifecycle check.
It fixes the previously failing test that verifies that if a boundary throws in its own componentWillMount(), the error will propagate.

* Remove extra whitespace
2016-10-15 18:13:56 +01:00
Sebastian Markbåge fda7a673c1 Fix whitespace in headers (#7980)
The script that strips providesModule is very sensitive.

Test plan:

Searched for providesModule in build. No more.

reactComponentExpect used to have problems too but doesn't seem
to anymore. Don't know why.
2016-10-14 17:46:34 -04:00
Nathan Hunzaker 0d20dcf910 Fix uncontrolled input decimal point "chopping" on number inputs, and validation warnings on email inputs (#7750)
* Only assign defaultValue if it has changed.

* Improve comment about reason for defaultValue conditional assignment
2016-10-13 08:20:14 -05:00
Kevin Huang b728fa0c17 Add warning for shady-dom use with rendered react components in DEV (#7911)
* Add warning for shady-dom use with rendered react components in DEV

* Make shady dom warning emit only once regardless of number of components

* Use didWarnShadyDom as warning argument & add missing shadyRoot check to ReactDOMComponent

* Add check for shady dom test to run only when createElement flag is true

* Pass false to warning in shadyDom warning check

* Add consistent DOM phrasing for shady DOM warnings

* Reference component name in shady DOM warning if it exists

* Add check for if owner exists in shady dom warning

* Add test for named component using createClass for shady DOM warning

* Clean up named component test for shady DOM warning

* Fix trailing comma linting issue on named shady DOM warning test
2016-10-12 10:13:50 +01:00
Kevin Huang 3b708728b0 Declare type var higher up in mountComponent() in ReactDOMComponent for reuse (#7944) 2016-10-12 10:12:15 +01:00
Islam Sharabash b77a96e496 Inject the previous batching strategy when rendering to string (#7930)
Before this change calling renderToStringImpl would inject
ReactDefaultBatchingStrategy after completion, even if a custom batching
strategy was injected before. This makes renderToStringImpl keep a reference to
the batching strategy before it runs and reinject it afterwards.
2016-10-11 12:57:22 -05:00
Toru Kobayashi 30067fa8ca Fix confusing variable names (#7863) 2016-10-04 12:53:29 +01:00
hkal f6fdfd1bf0 Add unknown property warning for use of autofocus (#7694) 2016-10-03 09:51:44 -05:00
Mark Penner dae3043897 Fixes #7824 (#7832) 2016-09-30 19:03:49 -07:00
J. Renée Beach 59ff7749ed React dom invalid aria hook (#7744)
* Add a hook that throws a runtime warning for invalid WAI ARIA attributes and values.

* Resolved linting errors.

* Added a test case for many props.

* Added a test case for ARIA attribute proper casing.

* Added a warning for uppercased attributes to ReactDOMInvalidARIAHook
2016-09-27 20:53:14 -05:00
Brandon Dail 7dfa01f9fa Revert ReactMultiChild to plain object (#7757)
Since ReactART and RN extend ReactMultiChild, changing it to a class is
a breaking change. See
https://github.com/facebook/react/pull/7736/files#r79073698
2016-09-17 10:40:23 -05:00
Uladzimir Havenchyk 65870dcaee Simplify event phases. Use explicitly 'captured' and 'bubbled'. (#7741) 2016-09-15 23:24:56 -07:00
Brandon Dail c78464f8ea Resolve flow errors with ReactTestRenderer (#7736)
* Resolve flow errors with ReactTestRenderer

* Add whitespace between types and methods

* extend ReactMultiChild instead of using Object.assign

* Use ReactElement type from ReactElementType

* Make ReactMultiChild a class
2016-09-15 11:44:56 -05:00
Brandon Dail 5a3abab660 Clean up ReactTestRenderer (#7716)
* create ReactTestTextComponent fil

* create ReactTestEmptyComponent

* Use class for ReactTestRenderer

* Add flow to ReactTestRenderer
2016-09-13 21:20:41 -05:00
Ben Alpert 9eba80825f Unify branches in cloneFiber (#7723) 2016-09-13 17:42:15 -07:00
Andrew Clark 6144212a86 [Fiber] Animation priority work (#7466)
* High priority work

Adds the ability to schedule and perform high priority work. In the
noop renderer, this is exposed using a method `performHighPriWork(fn)`
where the function is executed and all updates in that scope are given
high priority.

To do this, the scheduler keeps track of a default priority level.
A new function `performWithPriority(priority, fn)` changes the default
priority before calling the function, then resets it afterwards.

* Rename overloaded priority terms

"High" and "low" priority are overloaded terms. There are priority
levels called HighPriority and LowPriority. Meanwhile, there are
functions called {perform,schedule}HighPriWork, which corresponds
to requestAnimationFrame, and {perform,schedule}LowPriWork, which
corresponds to requestIdleCallback. But in fact, work that has
HighPriority is meant to be scheduled with requestIdleCallback.
This is super confusing.

To address this, {perform,schedule}HighPriWork has been renamed
to {perform,schedule}AnimationWork, and
{perform,schedule}LowPriWork has been renamed to
{perform,schedule}DeferredWork. HighPriority and LowPriority
remain the same.

* Priority levels merge fix
2016-09-13 16:46:32 -07:00
Sebastian Markbage 27d1210c1d Log the updateQueue in dumpTree
This also buffers all rows into a single console.log call.
This is because jest nows adds the line number of each console.log
call which becomes quite noisy for these trees.
2016-09-13 15:26:48 -07:00
Sebastian Markbage f3a2dc252e Check for truthiness of alternate
This is unfortunate since we agreed on using the `null | Fiber`
convention instead of `?Fiber` but haven't upgraded yet and this
is the pattern I've been using everywhere else so far.
2016-09-13 15:26:48 -07:00
Andrew Clark 0ca1cea26a Don't mutate current tree before work is committed.
We should be able to abort an update without any side-effects to the
current tree. This fixes a few cases where that was broken.

The callback list should only ever be set on the workInProgress.
There's no reason to add it to the current tree because they're not
needed after they are called during the commit phase.

Also found a bug where the memoizedProps were set to null in the
case of an update, because the pendingProps were null. Fixed by
transfering the props from the instance, like we were already doing
with state.

Added a test to ensure that setState can be called inside a
callback.
2016-09-13 15:26:48 -07:00
Andrew Clark f514662ca0 forceUpdate
Adds a field to the update queue that causes shouldComponentUpdate to
be skipped.
2016-09-13 15:26:48 -07:00
Andrew Clark d8c24cfa78 replaceState
Adds a field to UpdateQueue that indicates whether an update should
replace the previous state completely.
2016-09-13 15:26:48 -07:00
Andrew Clark 1d49237299 Update callbacks
Callbacks are stored on the same queue as updates. They care called
during the commit phase, after the updates have been flushed.

Because the update queue is cleared during the completion phase (before
commit), a new field has been added to fiber called callbackList. The
queue is transferred from updateQueue to callbackList during completion.
During commit, the list is reset.

Need a test to confirm that callbacks are not lost if an update is
preempted.
2016-09-13 15:26:47 -07:00
Andrew Clark c6db7f73be Rename stateQueue -> updateQueue
Also cleans up some types.
2016-09-13 15:26:47 -07:00
Andrew Clark f7dab22ac9 Ensure that setState update function's context is undefined 2016-09-13 15:26:47 -07:00
Andrew Clark 97dac74c40 Use ReactInstanceMap
Move ReactInstanceMap to src/renderers/shared/shared to indicate that
this logic is shared across implementations.
2016-09-13 15:26:47 -07:00
Andrew Clark d218158d22 Clean up
Use a union type for the head of StateQueue.
2016-09-13 15:26:47 -07:00