Commit Graph

321 Commits

Author SHA1 Message Date
Andrew Clark 1e3b6192b5 Import Scheduler directly, not via host config (#14984)
* Import Scheduler directly, not via host config

We currently schedule asynchronous tasks via the host config. (The host
config is a static/build-time dependency injection system that varies
across different renderers — DOM, native, test, and so on.) Instead of
calling platform APIs like `requestIdleCallback` directly, each renderer
implements a method called `scheduleDeferredCallback`.

We've since discovered that when scheduling tasks, it's crucial that
React work is placed in the same queue as other, non-React work on the
main thread. Otherwise, you easily end up in a starvation scenario where
rendering is constantly interrupted by less important tasks. You need a
centralized coordinator that is used both by React and by other
frameworks and application code. This coordinator must also have a
consistent API across all the different host environments, for
convention's sake and so product code is portable — e.g. so the same
component can work in both React Native and React Native Web.

This turned into the Scheduler package. We will have different builds of
Scheduler for each of our target platforms. With this approach, we treat
Scheduler like a built-in platform primitive that exists wherever React
is supported.

Now that we have this consistent interface, the indirection of the host
config no longer makes sense for the purpose of scheduling tasks. In
fact, we explicitly do not want renderers to scheduled task via any
system except the Scheduler package.

So, this PR removes `scheduleDeferredCallback` and its associated
methods from the host config in favor of directly importing Scheduler.

* Missed an extraneous export
2019-03-06 14:41:45 -08:00
Brian Vaughn a9aa24ed8d 16.8.4 and changelog 2019-03-05 15:17:42 -08:00
Andrew Clark 757a70b25d ReactNoop.yield -> Scheduler.yieldValue (#15008)
These used to be different things, but now ReactNoop.yield merely
re-exports Scheduler.yieldValue, so let's get rid of it.
2019-03-04 11:23:00 -08:00
Dan Abramov 02404d793b Avoid dynamic dispatch for scheduler calls (#14968) 2019-03-01 15:04:15 +00:00
Andrew Clark 69060e1da6 Swap expect(ReactNoop) for expect(Scheduler) (#14971)
* Swap expect(ReactNoop) for expect(Scheduler)

In the previous commits, I upgraded our custom Jest matchers for the
noop and test renderers to use Scheduler under the hood.

Now that all these matchers are using Scheduler, we can drop
support for passing ReactNoop and test roots and always pass
Scheduler directly.

* Externalize Scheduler in noop and test bundles

I also noticed we don't need to regenerator runtime in noop anymore.
2019-02-28 12:54:47 -08:00
Andrew Clark ccb2a8a44e Replace test renderer's fake Scheduler implementation with mock build (#14970)
* Replace test renderer's fake Scheduler implementation with mock build

The test renderer has its own mock implementation of the Scheduler
interface, with the ability to partially render work in tests. Now that
this functionality has been lifted into a proper mock Scheduler build,
we can use that instead.

* Fix Profiler tests in prod
2019-02-28 10:50:38 -08:00
Andrew Clark 53e787b45f Replace noop's fake Scheduler implementation with mock Scheduler build (#14969)
* Replace noop's fake Scheduler implementation with mock Scheduler build

The noop renderer has its own mock implementation of the Scheduler
interface, with the ability to partially render work in tests. Now that
this functionality has been lifted into a proper mock Scheduler build,
we can use that instead.

Most of the existing noop tests were unaffected, but I did have to make
some changes. The biggest one involved passive effects: previously, they
were scheduled on a separate queue from the queue that handles
rendering. After this change, both rendering and effects are scheduled
in the Scheduler queue. I think this is a better approach because tests
no longer have to worry about the difference; if you call `flushAll`,
all the work is flushed, both rendering and effects. But for those few
tests that do care to flush the rendering without the effects, that's
still possible using the `yieldValue` API.

Follow-up: Do the same for test renderer.

* Fix import to scheduler/unstable_mock
2019-02-28 10:30:46 -08:00
Andrew Clark 00748c53e1 Add new mock build of Scheduler with flush, yield API (#14964)
* Add new mock build of Scheduler with flush, yield API

Test environments need a way to take control of the Scheduler queue and
incrementally flush work. Our current tests accomplish this either using
dynamic injection, or by using Jest's fake timers feature. Both of these
options are fragile and rely too much on implementation details.

In this new approach, we have a separate build of Scheduler that is
specifically designed for test environments. We mock the default
implementation like we would any other module; in our case, via Jest.
This special build has methods like `flushAll` and `yieldValue` that
control when work is flushed. These methods are based on equivalent
methods we've been using to write incremental React tests. Eventually
we may want to migrate the React tests to interact with the mock
Scheduler directly, instead of going through the host config like we
currently do.

For now, I'm using our custom static injection infrastructure to create
the two builds of Scheduler — a default build for DOM (which falls back
to a naive timer based implementation), and the new mock build. I did it
this way because it allows me to share most of the implementation, which
isn't specific to a host environment — e.g. everything related to the
priority queue. It may be better to duplicate the shared code instead,
especially considering that future environments (like React Native) may
have entirely forked implementations. I'd prefer to wait until the
implementation stabilizes before worrying about that, but I'm open to
changing this now if we decide it's important enough.

* Mock Scheduler in bundle tests, too

* Remove special case by making regex more restrictive
2019-02-26 20:51:17 -08:00
Andrew Clark 8e25ed20bd Unify noop and test renderer assertion APIs (#14952)
* Throw in tests if work is done before emptying log

Test renderer already does this. Makes it harder to miss unexpected
behavior by forcing you to assert on every logged value.

* Convert ReactNoop tests to use jest matchers

The matchers warn if work is flushed while the log is empty. This is
the pattern we already follow for test renderer. I've used the same APIs
as test renderer, so it should be easy to switch between the two.
2019-02-25 19:01:45 -08:00
Dan Abramov 8c1966590a Release 16.8.3 2019-02-21 18:09:18 +00:00
Dan Abramov 7de4d23919 Fix UMD builds by re-exporting the scheduler priorities (#14914) 2019-02-21 17:20:28 +00:00
Nathan Hunzaker d0318fb3f9 Updating copyright headers, dropping the year (#14893)
* Updating copyright headers, dropping the year
* Update copyright in ReactDOMHooks-test and react-cache LRU.js
2019-02-21 08:46:13 -08:00
Dan Abramov 3e55560438 Release 16.8.2 2019-02-14 19:13:15 +00:00
Brian Vaughn 45fc46bfa0 16.8.1 packages 2019-02-06 18:21:33 +00:00
Andrew Clark bc9818f24d Scheduler.unstable_next (#14756)
* Add Scheduler.unstable_next

* Use Scheduler to prioritize updates

Changes the implementation of syncUpdates, deferredUpdates, and
interactiveUpdates to use runWithPriority, so

This is the minimum integration between Scheduler and React needed to
unblock use of the Scheduler.next API.

* Add Scheduler.unstable_next

* Use Scheduler to prioritize updates

Changes the implementation of syncUpdates, deferredUpdates, and
interactiveUpdates to use runWithPriority, so

This is the minimum integration between Scheduler and React needed to
unblock use of the Scheduler.next API.
2019-02-06 08:16:41 +00:00
Brian Vaughn 008a2ab9cd 16.8.0 2019-02-06 08:04:00 +00:00
Dan Abramov c21c41ecfa Tweak invalid Hook warning and error (#14747) 2019-02-01 21:05:13 +00:00
Andrew Clark 66eb293742 Restrict effect return type to a function or nothing (#14119)
* Restrict effect return type to a function or nothing

We already warn in dev if the wrong type is returned. This updates the
Flow type.

* Restrict return type further

* Assume Effect hook returns either a function or undefined

* Tweak warning message
2019-01-31 10:11:47 -08:00
Dan Abramov 51c07912ac Warn when second argument is passed to useCallback (#14729) 2019-01-31 13:56:48 +00:00
Andrew Clark ba6477aa3c Improve Reducer Hook's lazy init API (#14723)
* Improve Reducer Hook's lazy init API

* Use generic type for initilizer input

Still requires an `any` cast in the case where `init` function is
not provided.
2019-01-29 17:39:24 -08:00
Andrew Clark cb1ff430e8 Phased dispatcher (#14701)
* Move DEV-only function right above where it's used

I don't like looking at this top-level function #petty

* Use different dispatchers for functions & classes

Classes support readContext, but not any of the other dispatcher
methods. Function support all methods.

This is a more robust version of our previous strategy of checking
whether `currentlyRenderingFiber` is null.

As a next step, we can use a separate dispatcher for each phase of the
render cycle (mount versus update).

* Use separate dispatchers for mount and update

* Remove mount code from update path

Deletes mount-specific code from the update path, since it should be
unreachable. To continue supporting progressive enhancement (mounting
new hooks at the end of the list), we detect when there are no more
current hooks and switch back to the mount dispatcher. Progressive
enhancement isn't officially supported yet, so it will continue to warn.

* Factoring nits

* Fix Flow

Had to cheat more than I would like

* More Flow nits

* Switch back to using a special dispatcher for nested hooks in DEV

In order for this strategy to work, I had to revert progressive
enhancement support (appending hooks to the end). It was previously a
warning but now it results in an error. We'll reconsider later.

* Always pass args to updateState and updateReducer

Even though the extra args are only used on mount, to ensure
type consistency.
2019-01-29 16:32:15 -08:00
Brian Vaughn 6cb26774e2 Enable hooks! (#14679)
* Turned enableHooks feature flag on everywhere
* Removed useHooks feature flag from tests (now that it's on by default)
* Remove useHooks feature flag entirely
2019-01-23 13:28:09 -08:00
Grey Baker b66e6e41e6 Add directory details to the package.json of all packages (#14628)
Specifying the directory as part of the `repository` field in a `package.json`
allows third party tools to provide better support when working with monorepos.
For example, it allows them to correctly construct a commit diff for a specific
package.

This format was accepted by npm in https://github.com/npm/rfcs/pull/19.
2019-01-18 20:21:12 +00:00
Brian Vaughn edb1f59564 Support configurable labels for custom hooks (#14559)
* react-debug-tools accepts currentDispatcher ref as param

* ReactDebugHooks injected dispatcher ref is optional

* Support custom values for custom hooks

* PR feedback:

1. Renamed useDebugValueLabel hook to useDebugValue
2. Wrapped useDebugValue internals in if-DEV so that it could be removed from production builds.

* PR feedback:

1. Fixed some minor typos
2. Added inline comment explaining the purpose of  rollupDebugValues()
3. Refactored rollupDebugValues() to use a for loop rather than filter()
4. Improve check for useDebugValue hook to lessen the chance of a false positive
5. Added optional formatter function param to useDebugValue

* Nitpick renamed a method
2019-01-14 14:53:22 -08:00
Sunil Pai 3e15b1c690 make a fork for ReactCurrentDispatcher (#14588) 2019-01-14 16:35:56 +00:00
Sunil Pai b4ad8e9471 rename useImperativeMethods -> useImperativeHandle (#14565) 2019-01-10 13:37:50 +00:00
Brian Vaughn 19ef0ec116 Separate current owner and dispatcher (#14548) 2019-01-08 14:39:52 -08:00
Sophie Alpert 547e059f0b Simplify wording of key warning (#14503)
I don't think "array or iterator" is adding anything, and it may well be confusing, especially since this is one of the first and most common warnings that devs see.
2019-01-07 08:30:23 -08:00
Andrew Clark c695b2384f React v16.7.0 2018-12-19 17:23:14 -08:00
Andrew Clark 4a1072194f Memoize promise listeners to prevent exponential growth (#14429)
* Memoize promise listeners to prevent exponential growth

Previously, React would attach a new listener every time a promise is
thrown, regardless of whether the same listener was already attached
during a previous render. Because React attempts to render every time
a promise resolves, the number of listeners grows quickly.

This was especially bad in synchronous mode because the renders that
happen when the promise pings are not batched together. So if a single
promise has multiple listeners for the same root, there will be multiple
renders, which in turn results in more listeners being added to the
remaining unresolved promises. This results in exponential growth in
the number of listeners with respect to the number of IO-bound
components in a single render.

Fixes #14220

* Memoize on the root and Suspense fiber instead of on the promise

* Add TODO to fix persistent mode tests
2018-12-14 11:03:23 -08:00
Kevin Chavez 8df4d59be5 Implement pauseExecution, continueExecution, dumpQueue for Scheduler (#14053)
* Implement pauseExecution, continueExecution, dumpQueue

* Expose firstCallbackNode. Fix tests. Revert results.json

* Put scheduler pausing behind a feature flag
2018-12-06 13:57:23 -08:00
Dan Abramov d14ba87b1b Validate propTypes for lazy() and memo() and warn about invalid patterns (#14298)
* Add a test for current defaultProps behavior in lazy

* Add a warning against definining defaultProps on the outer wrapper

* Warn about setting propTypes too

* Remove redundant async

* Validate propTypes for resolved lazy types

Note this only works for elements created after resolving. So it's not ideal. But it provides the best stack trace for those cases.

* Add a test for lazy(forwardRef()) propTypes check

* Validate memo() inner propTypes and warn about shadowing

* Add test verifying nested lazy is unsupported

* Change error wording to remove "Promise elements"

* Improve error message for nested lazy() and add tests

* Validate propTypes for memo in the reconciler when necessary

* Add comments for why we're calling checkPropTypes

* Fix Flow and lint

* Undo unintentional formatting changes

* Remove unnecessary case (it is handled by function code path)

* Add test coverage for memo(fn.defaultProps).propTypes

* Test should be agnostic of where resolving happens

That's an implementation detail and we might want to change it later. Let's keep it easy by making tests just check that validation happened, not at which stage.

* Unify traversal logic in createElement

This moves all type traversal into createElement. When lazy resolves, we call createElement once to re-check.

* Match prod behavior for propTypes/defaultProps shims closer

* Revert "Unify traversal logic in createElement"

This reverts commit 2e77ca47fe.

See https://github.com/facebook/react/pull/14298#issuecomment-442687775

* Undo unnecessary change to getComponentName
2018-11-29 20:06:28 +00:00
Sophie Alpert c2a2d8a539 Remove useMutationEffect (#14336)
useMutationEffect has problems (namely, refs aren't attached at the time that it runs) and we're not positive it's necessary. useLayoutEffect runs at the same time as componentDidMount/Update so it's sufficient for all existing use cases; it can be used in any case that useEffect happens too late. Until we figure out what we want to do, let's delete it.
2018-11-27 13:05:13 -08:00
Brian Vaughn 686f1060ad Publish a local release (canary or stable) to NPM (#14260)
New release scripts.

Learn more at https://github.com/facebook/react/blob/master/scripts/release/README.md
2018-11-23 12:37:18 -08:00
Dan Abramov 7475120ce7 Prevent deopts from modifying exports object in stable builds (#14309) 2018-11-23 14:10:57 +00:00
Sebastian Markbåge 961eb65b4b Use unique thread ID for each partial render to access Context (#14182)
* BUG: ReactPartialRenderer / New Context polutes mutable global state

The new context API stores the provided values on the shared context instance. When used in a synchronous context, this is not an issue. However when used in an concurrent context this can cause a "push provider" from one react render to have an effect on an unrelated concurrent react render.

I've encountered this bug in production when using renderToNodeStream, which asks ReactPartialRenderer for bytes up to a high water mark before yielding. If two Node Streams are created and read from in parallel, the state of one can polute the other.

I wrote a failing test to illustrate the conditions under which this happens.

I'm also concerned that the experimental concurrent/async React rendering on the client could suffer from the same issue.

* Use unique thread ID for each partial render to access Context

This first adds an allocator that keeps track of a unique ThreadID index
for each currently executing partial renderer. IDs are not just growing
but are reused as streams are destroyed.

This ensures that IDs are kept nice and compact.

This lets us use an "array" for each Context object to store the current
values. The look up for these are fast because they're just looking up
an offset in a tightly packed "array".

I don't use an actual Array object to store the values. Instead, I rely
on that VMs (notably V8) treat storage of numeric index property access
as a separate "elements" allocation.

This lets us avoid an extra indirection.

However, we must ensure that these arrays are not holey to preserve this
feature.

To do that I store the _threadCount on each context (effectively it takes
the place of the .length property on an array).

This lets us first validate that the context has enough slots before we
access the slot. If not, we fill in the slots with the default value.
2018-11-09 15:38:20 -08:00
Andrew Clark b50e63ef53 Updating package versions for release 16.6.1 2018-11-06 18:19:57 -08:00
Andrew Clark e9a2ec9156 [suspense] Avoid double commit by re-rendering immediately and reusing primary children (#14083)
* Avoid double commit by re-rendering immediately and reusing children

To support Suspense outside of concurrent mode, any component that
starts rendering must commit synchronously without being interrupted.
This means normal path, where we unwind the stack and try again from the
nearest Suspense boundary, won't work.

We used to have a special case where we commit the suspended tree in an
incomplete state. Then, in a subsequent commit, we re-render using the
fallback.

The first part — committing an incomplete tree — hasn't changed with
this PR. But I've changed the second part — now we render the fallback
children immediately, within the same commit.

* Add a failing test for remounting fallback in sync mode

* Add failing test for stuck Suspense fallback

* Toggle visibility of Suspense children in mutation phase, not layout

If parent reads visibility of children in a lifecycle, they should have
already updated.
2018-11-05 16:32:50 -08:00
Simen Bekkhus 8eca0eff87 chore(tests): don't rely on jest fake timers scheduling real timers (#14003)
* chore: don't rely on jest fake timers scheduling real timers

* re-add one part not working with Jest 23
2018-11-02 16:54:23 -05:00
Sophie Alpert c898020e01 Warn for forwardRef(memo(...)) (#14070)
People are probably gonna do this all the time.
2018-11-02 12:32:44 -07:00
Andrew Clark 8c67bbf183 [scheduler] Deadline object -> shouldYield (#14025)
* [scheduler] Deadline object -> shouldYield

Instead of using a requestIdleCallback-style deadline object, expose a
method Scheduler.shouldYield that returns true if there's a higher
priority event in the queue.

* Nits
2018-10-30 13:47:51 -07:00
Andrew Clark 5045763064 Swap order of function member in hook union types 2018-10-29 11:42:41 -07:00
Sophie Alpert 5fc84efacc Skip updating effect tag when skipping effect
For example, if you have `useEffect(..., [])`, there's no need to set .effectTag to `Update | Passive` on updates.
2018-10-29 11:26:54 -07:00
Dan Abramov f7cb9d2b22 Warn about useContext(Consumer|Provider) 2018-10-29 11:26:54 -07:00
Sebastian Markbåge b772e0e26b "functional component" -> "function component" in hooks error messages 2018-10-29 11:26:53 -07:00
Andrew Clark 11d0781eea Defer useEffect until after paint
Effects scheduled by useEffect should not fire until after the browser
has had a chance to paint. However, they should be fired before any
subsequent mutations.

Also adds useMutationEffect and useLayoutEffect. useMutationEffect fires
during the host update phase. useLayoutEffect fires during the post-
update phase (the same phase as componentDidMount
and componentDidUpdate).
2018-10-29 11:26:53 -07:00
Andrew Clark 105f2de545 Put hooks behind feature flag 2018-10-29 11:26:53 -07:00
Andrew Clark 7bee9fbdd4 Initial hooks implementation
Includes:
- useState
- useContext
- useEffect
- useRef
- useReducer
- useCallback
- useMemo
- useAPI
2018-10-29 11:26:53 -07:00
Joseph 95f98a1873 fix typo (#13955) 2018-10-27 09:59:00 -07:00
Brian Vaughn 275e76e83b Enable stable concurrent APIs flag for 16.7 alpha (#13928)
* Add enableStableConcurrentModeAPIs feature flag

* Conditionally name concurrent API based on enableStableConcurrentModeAPIs flag
2018-10-24 13:45:07 -07:00