Commit Graph
1424 Commits
Author SHA1 Message Date
Brian Vaughn 505c15c9ef Don't inject timeline hooks unless React supports profiling (#23151)
This gives DevTools a way to detect whether the current React renderer supports Timeline profiling. (Version alone isn't enough to detect this, neither is general profiling support– since these two are controlled by different feature flags.)
2022-01-20 11:11:26 -05:00
Douglas ArmstrongandDan Abramov e12a9dfc96 Fix production-only updateSyncExternalStore() crash when doing setState in render (#23150)
* Update ReactFiberHooks.new.js

* Add regression test + replace-fork

* Prettier

Co-authored-by: Dan Abramov <dan.abramov@me.com>
2022-01-20 15:22:17 +00:00
Dan Abramov e489402557 Warn when a callback ref returns a function (#23145) 2022-01-20 15:18:38 +00:00
Dan Abramovandoverlookmotel d8cfeaf221 Fix context propagation for offscreen/fallback trees (#23095)
* Failing test for Context.Consumer in suspended Suspense

See issue #19701.

* Fix context propagation for offscreen trees

* Address nits

* Specify propagation root for Suspense too

* Pass correct propagation root

* Harden test coverage

This test will fail if we remove propagation, or if we propagate with a root node like fiber.return or fiber.return.return. The additional DEV-only error helps detect a different kind of mistake, like if the thing being passed hasn't actually been encountered on the way up. However, we still leave the actual production loop to check against null so that there is no way we loop forever if the propagation root is wrong.

* Remove superfluous warning

Co-authored-by: overlookmotel <theoverlookmotel@gmail.com>
2022-01-19 16:30:34 +00:00
Brian Vaughn d504824780 Enable scheduling profiler flag in react-dom/testing builds (#23142) 2022-01-19 11:20:42 -05:00
Dan Abramov 790b5246f6 Fix setState ignored in Safari when iframe is added to DOM in the same commit (#23111)
* Fix setState being ignored in Safari

* Add a regression test

* Add comment
2022-01-18 18:25:34 +00:00
Brian Vaughn 51947a14bb Refactored how React/DevTools log Timeline performance data (#23102)
Until now, DEV and PROFILING builds of React recorded Timeline profiling data using the User Timing API. This commit changes things so that React records this data by calling methods on the DevTools hook. (For now, DevTools still records that data using the User Timing API, to match previous behavior.)

This commit is large but most of it is just moving things around:

* New methods have been added to the DevTools hook (in "backend/profilingHooks") for recording the Timeline performance events.
* Reconciler's "ReactFiberDevToolsHook" has been updated to call these new methods (when they're present).
* User Timing method calls in "SchedulingProfiler" have been moved to DevTools "backend/profilingHooks" (to match previous behavior, for now).
* The old reconciler tests, "SchedulingProfiler-test" and "SchedulingProfilerLabels-test", have been moved into DevTools "TimelineProfiler-test" to ensure behavior didn't change unexpectedly.
* Two new methods have been added to the injected renderer interface: injectProfilingHooks() and getLaneLabelMap().

Relates to #22529.
2022-01-13 14:55:54 -05:00
Sebastian Markbåge ceee524a8f Remove unnecessary clearContainer call (#22979)
This was added when we added error recovery for hydration errors. However,
when the fix up pass happens later on, it'll still call clearContainer in
the commit phase. So this call is unnecessary.
2021-12-17 09:40:39 -08:00
salazarm f2a59df48b Remove unstableAvoidThisFallback from OSS (#22884)
* avoidThisFallbackFlag

* avoidThisFallback flag

* missed a spot

* rm gating
2021-12-08 08:21:02 -08:00
salazarm 5041c37d27 Remove hydrate option from createRoot (#22878)
* remove hydrate: true option

* remove missed comment

* lint

* warning

* circumvent flow
2021-12-07 16:10:00 -05:00
Andrew Clark 4729ff6d1f Implement identifierPrefix option for useId (#22855)
When an `identifierPrefix` option is given, React will add it to the
beginning of ids generated by `useId`.

The main use case is to avoid conflicts when there are multiple React
roots on a single page.

The server API already supported an `identifierPrefix` option. It's not
only used by `useId`, but also for React-generated ids that are used to
stitch together chunks of HTML, among other things. I added a
corresponding option to the client.

You must pass the same prefix option to both the server and client.
Eventually we may make this automatic by sending the prefix from the
server as part of the HTML stream.
2021-12-02 17:49:43 -08:00
salazarm c1220ebdde treat empty string as null (#22807) 2021-11-23 18:40:10 -05:00
salazarm fdc1d617a4 Flag for client render fallback behavior on hydration mismatch (#22787)
* Add flag for new client-render fallback behavior on hydration mismatch

* gate test

* gate tests too

* fix test gating
2021-11-18 08:16:09 -05:00
Andrew Clark 83564712b6 Move SuspenseList to experimental channel (#22765)
There's more work to be done to implement this correctly on the server,
so we're going to wait to release it until an 18.x minor.
2021-11-15 10:12:56 -08:00
Brijesh Prasad d4144e6e54 fix : grammatical typo for test description (#22764)
* fix : grammatical typo for test description

* fix linting issues
2021-11-15 11:44:48 -05:00
Han Han 0b329511b9 chore: fix comment typo (#22657) 2021-11-15 10:58:30 -05:00
180909 e6f60d2add fix typos (#22715) 2021-11-15 10:58:21 -05:00
Brian Vaughn cb11155c86 Add runtime type checks around module boundary code (#22748) 2021-11-12 10:03:41 -05:00
salazarm 0ddd69d122 Throw on hydration mismatch and force client rendering if boundary hasn't suspended within concurrent root (#22629)
* Throw on hydration mismatch

* remove debugger

* update error message

* update error message part2...

* fix test?

* test? :(

* tests 4real

* remove useRefAccessWarning gating

* split markSuspenseBoundary and getNearestBoundary

* also assert html is correct

* replace-fork

* also remove client render flag on suspend

* replace-fork

* fix mismerge????
2021-11-09 13:40:50 -05:00
Brian Vaughn 1bf6deb865 Renamed packages/react-devtools-scheduling-profiler to packages/react-devtools-timeline (#22691) 2021-11-04 10:02:06 -04:00
Brian Vaughn 51c558aeb6 Rename (some) "scheduling profiler" references to "timeline" (#22690) 2021-11-03 15:10:29 -04:00
Andrew Clark 00ced1e2b7 Fix useId in strict mode (#22681)
* Fix: useId in strict mode

In strict mode, `renderWithHooks` is called twice to flush out
side effects.

Modying the tree context (`pushTreeId` and `pushTreeFork`) is effectful,
so before this fix, the tree context was allocating two slots for a
materialized id instead of one.

To address, I lifted those calls outside of `renderWithHooks`. This
is how I had originally structured it, and it's how Fizz is structured,
too. The other solution would be to reset the stack in between the calls
but that's also a bit weird because we usually only ever reset the
stack during unwind or complete.

* Add test for render phase updates

Noticed this while fixing the previous bug
2021-11-02 17:59:17 -07:00
Andrew Clark 75f3ddebfa Remove experimental useOpaqueIdentifier API (#22672)
useId is the updated version of this API.
2021-11-01 15:02:39 -07:00
Andrew Clark ebf9ae8579 useId (#22644)
* Add useId to dispatcher

* Initial useId implementation

Ids are base 32 strings whose binary representation corresponds to the
position of a node in a tree.

Every time the tree forks into multiple children, we add additional bits
to the left of the sequence that represent the position of the child
within the current level of children.

    00101       00010001011010101
    ╰─┬─╯       ╰───────┬───────╯
  Fork 5 of 20       Parent id

The leading 0s are important. In the above example, you only need 3 bits
to represent slot 5. However, you need 5 bits to represent all the forks
at the current level, so we must account for the empty bits at the end.

For this same reason, slots are 1-indexed instead of 0-indexed.
Otherwise, the zeroth id at a level would be indistinguishable from
its parent.

If a node has only one child, and does not materialize an id (i.e. does
not contain a useId hook), then we don't need to allocate any space in
the sequence. It's treated as a transparent indirection. For example,
these two trees produce the same ids:

<>                          <>
  <Indirection>               <A />
    <A />                     <B />
  </Indirection>            </>
  <B />
</>

However, we cannot skip any materializes an id. Otherwise, a parent id
that does not fork would be indistinguishable from its child id. For
example, this tree does not fork, but the parent and child must have
different ids.

<Parent>
  <Child />
</Parent>

To handle this scenario, every time we materialize an id, we allocate a
new level with a single slot. You can think of this as a fork with only
one prong, or an array of children with length 1.

It's possible for the the size of the sequence to exceed 32 bits, the
max size for bitwise operations. When this happens, we make more room by
converting the right part of the id to a string and storing it in an
overflow variable. We use a base 32 string representation, because 32 is
the largest power of 2 that is supported by toString(). We want the base
to be large so that the resulting ids are compact, and we want the base
to be a power of 2 because every log2(base) bits corresponds to a single
character, i.e. every log2(32) = 5 bits. That means we can lop bits off
the end 5 at a time without affecting the final result.

* Incremental hydration

Stores the tree context on the dehydrated Suspense boundary's state
object so it resume where it left off.

* Add useId to react-debug-tools

* Add selective hydration test

Demonstrates that selective hydration works and ids are preserved even
after subsequent client updates.
2021-11-01 13:30:44 -07:00
Andrew Clark a0d991fe65 Re-land #22292 (remove uMS from open source build) (#22664)
I had to revert #22292 because there are some internal callers of
useMutableSource that we haven't migrated yet. This removes
useMutableSource from the open source build but keeps it in the
internal one.
2021-10-31 21:39:51 -07:00
Andrew Clark 6bce0355c3 Upgrade useSyncExternalStore to alpha channel (#22662)
* Move useSyncExternalStore shim to a nested entrypoint

Also renames `useSyncExternalStoreExtra` to
`useSyncExternalStoreWithSelector`.

- 'use-sync-external-store/shim' -> A shim for `useSyncExternalStore`
  that works in React 16 and 17 (any release that supports hooks). The
  module will first check if the built-in React API exists, before
  falling back to the shim.
- 'use-sync-external-store/with-selector' -> An extended version of
  `useSyncExternalStore` that also supports `selector` and `isEqual`
  options. It does _not_ shim `use-sync-external-store`; it composes the
  built-in React API. **Use this if you only support 18+.**
- 'use-sync-external-store/shim/with-selector' -> Same API, but it
  composes `use-sync-external-store/shim` instead. **Use this for
  compatibility with 16 and 17.**
- 'use-sync-external-store' -> Re-exports React's built-in API. Not
  meant to be used. It will warn and direct users to either the shim or
  the built-in API.

* Upgrade useSyncExternalStore to alpha channel
2021-10-31 15:38:03 -07:00
Andrew Clark 3c4c1c4703 Remove warning for dangling passive effects (#22609)
In legacy mode, a test can get into a situation where passive effects are
"dangling" — an update finished, and scheduled some passive effects,
but the effects don't flush.

This is why React warns if you don't wrap updates in act. The act API is
responsible for flushing passive effects. But there are some cases where
the act API (in legacy roots) intentionally doesn't warn, like updates
that originate from roots and classes. It's possible those updates will
render children that contain useEffect. Because of this, dangling
effects are still possible, and React doesn't warn about it.

So we implemented a second act warning for dangling effects.

However, in concurrent roots, we now enforce that all APIs that schedule
React work must be wrapped in act. There's no scenario where dangling
passive effects can happen that doesn't already trigger the warning for
updates. So the dangling effects warning is redundant.

The warning was never part of a public release. It was only enabled
in concurrent roots.

So we can delete it.
2021-10-21 15:39:59 -07:00
Andrew Clark d5b6b4b865 Expand act warning to cover all APIs that might schedule React work (#22607)
* Move isActEnvironment check to function that warns

I'm about to fork the behavior in legacy roots versus concurrent roots
even further, so I'm lifting this up so I only have to fork once.

* Lift `mode` check, too

Similar to previous commit. I only want to check this once. Not for
performance reasons, but so the logic is easier to follow.

* Expand act warning to include non-hook APIs

In a test environment, React warns if an update isn't wrapped with act
— but only if the update originates from a hook API, like useState.

We did it this way for backwards compatibility with tests that were
written before the act API was introduced. Those tests didn't require
act, anyway, because in a legacy root, all tasks are synchronous except
for `useEffect`.

However, in a concurrent root, nearly every task is asynchronous. Even
tasks that are synchronous may spawn additional asynchronous work. So
all updates need to be wrapped with act, regardless of whether they
originate from a hook, a class, a root, or any other type of component.

This commit expands the act warning to include any API that triggers
an update.

It does not currently account for renders that are caused by a Suspense
promise resolving; those are modelled slightly differently from updates.
I'll fix that in the next step.

I also removed the check for whether an update is batched. It shouldn't
matter, because even a batched update can spawn asynchronous work, which
needs to be flushed by act.

This change only affects concurrent roots. The behavior in legacy roots
is the same.

* Expand act warning to include Suspense resolutions

For the same reason we warn when an update is not wrapped with act,
we should warn if a Suspense promise resolution is not wrapped with act.
Both "pings" and "retries".

Legacy root behavior is unchanged.
2021-10-21 15:31:26 -07:00
Joseph Savona fa9bea0c41 Initial implementation of cache cleanup (#22510)
This is an initial, partial implementation of a cleanup mechanism for the experimental Cache API. The idea is that consumers of the Cache API can register to be informed when a given Cache instance is no longer needed so that they can perform associated cleanup tasks to free resources stored in the cache. A canonical example would be cancelling pending network requests.

An overview of the high-level changes:

* Changes the `Cache` type from a Map of cache instances to be an object with the original Map of instances, a reference count (to count roughly "active references" to the cache instances - more below), and an AbortController.
* Adds a new public API, `unstable_getCacheSignal(): AbortSignal`, which is callable during render. It returns an AbortSignal tied to the lifetime of the cache - developers can listen for the 'abort' event on the signal, which React now triggers when a given cache instance is no longer referenced. 
  * Note that `AbortSignal` is a web standard that is supported by other platform APIs; for example a signal can be passed to `fetch()` to trigger cancellation of an HTTP request.
* Implements the above - triggering the 'abort' event - by handling passive mount/unmount for HostRoot and CacheComponent fiber nodes.

Cases handled:
* Aborted transitions: we clean up a new cache created for an aborted transition
* Suspense: we retain a fresh cache instance until a suspended tree resolves

For follow-ups:
* When a subsequent cache refresh is issued before a previous refresh completes, the refreshes are queued. Fresh cache instances for previous refreshes in the queue should be cleared, retaining only the most recent cache. I plan to address this in a follow-up PR.
* If a refresh is cancelled, the fresh cache should be cleaned up.
2021-10-21 14:11:42 -07:00
Brian Vaughn 0e8a5aff3d Scheduling Profiler: Add marks for component effects (mount and unmount) (#22578)
DevTools (and its profilers) should not require users to be familiar with React internals. Although the scheduling profiler includes a CPU sample flame graph, it's there for advanced use cases and shouldn't be required to identify common performance issues.

This PR proposes adding new marks around component effects. This will enable users to identify components with slow effect create/destroy functions without requiring them to dig through the call stack. (Once #22529 lands, these new marks will also include component stacks, making them more useful still.)

For example, here's a profile with a long-running effect. Without this change, it's not clear why the effects phase takes so long. After this change, it's more clear why that the phase is longer because of a specific component.

We may consider adding similar marks around render phase hooks like useState, useReducer, useMemo. I avoided doing that in this PR because it would be a pretty pervasive change to the ReactFiberHooks file.

Note that this change should have no effect on production bundles since everything is guarded behind a profiling feature flag.

Going to tag more people than I normally would for this pR, since it touches both reconciler and DevTools packages. Feel free to ignore though if you don't have strong feelings.

Note that although this PR adds new marks to the scheduling profiler, it's done in a way that's backwards compatible for older profiles.
2021-10-21 14:41:44 -04:00
Brian Vaughn 4ba20579da Scheduling Profiler: De-emphasize React internal frames (#22588)
This commit adds code to all React bundles to explicitly register the beginning and ending of the module. This is done by creating Error objects (which capture the file name, line number, and column number) and passing them explicitly to a DevTools hook (when present).

Next, as the Scheduling Profiler logs metadata to the User Timing API, it prints these module ranges along with other metadata (like Lane values and profiler version number).

Lastly, the Scheduling Profiler UI compares stack frames to these ranges when drawing the flame graph and dims or de-emphasizes frames that fall within an internal module.

The net effect of this is that user code (and 3rd party code) stands out clearly in the flame graph while React internal modules are dimmed.

Internal module ranges are completely optional. Older profiling samples, or ones recorded without the React DevTools extension installed, will simply not dim the internal frames.
2021-10-21 14:40:41 -04:00
Sebastian Markbåge 34e4c97561 Clear extra nodes if there's a hydration mismatch within a suspense boundary (#22592)
* Clear extra nodes if there's a mismatch within a suspense boundary

This usually happens when we exit out a DOM node but a suspense boundary
is a virtual DOM node and we didn't do it in that case because we took a
short cut by calling resetHydrationState directly since we know we won't
need to pop.

* Tighten up the types of getFirstHydratableChild

We currently call getFirstHydratableChild to step into the children of
a suspense boundary. This can be a text node or a suspense boundary
which isn't compatible with getFirstHydratableChild, and we cheat the type.

This accidentally works because .firstChild always returns null on those
nodes in the DOM.

This just makes that explicit.
2021-10-19 16:28:01 -07:00
Andrew Clark 02f411578a Upgrade useInsertionEffect to stable (#22589)
@huozhi tried this out and says it's working as expected. I think we
can go ahead and move this into the stable channel, so that it is
available in the React 18 alpha releases.
2021-10-19 14:32:54 -07:00
Sebastian MarkbågeandDan Abramov 2af4a79333 Hydrate using SuspenseComponent as the parent (#22582)
* Add a failing test for Suspense hydration

* Include salazarm's changes to the test

* The hydration parent of a suspense boundary should be the boundary itself

This eventually got set when we popped back out of its children but it
doesn't start out that way.

This fixes it so that the boundary parent is always the suspense boundary.

* We now need to log errors with a suspense boundary as a parent

For now, we just log this with commentNode.parentNode as the parent for
purposes of the error message.

* Make a special getFirstHydratableChildWithinSuspenseInstance

We currently call getNextHydratableSibling but conceptually it's the child
of the boundary. It just happens to be that when we use comment nodes, we
need to call nextSibling in the DOM.

This makes this separation a bit clearer.

* Sync old fork

Co-authored-by: Dan Abramov <dan.abramov@me.com>
2021-10-18 17:44:34 -07:00
Andrew Clark 996da67b29 Replace global jest heuristic with IS_REACT_ACT_ENVIRONMENT (#22562)
* Remove `jest` global check in concurrent roots

In concurrent mode, instead of checking `jest`, we check the new
`IS_REACT_ACT_ENVIRONMENT` global. The default behavior is `false`.

Legacy mode behavior is unchanged.

React's own internal test suite use a custom version of `act` that works
by mocking the Scheduler — rather than the "real" act used publicly. So
we don't enable the flag in our repo.

* Warn if `act` is called in wrong environment

Adds a warning if `act` is called but `IS_REACT_ACT_ENVIRONMENT` is not
enabled. The goal is to prompt users to correctly configure their
testing environment, so that if they forget to use `act` in a different
test, we can detect and warn about.

It's expected that the environment flag will be configured by the
testing framework. For example, a Jest plugin. We will link to the
relevant documentation page, once it exists.

The warning only fires in concurrent mode. Legacy roots will keep the
existing behavior.
2021-10-18 08:59:18 -07:00
Andrew Clark 163e81c1f8 Support disabling spurious act warnings with a global environment flag (#22561)
* Extract `act` environment check into function

`act` checks the environment to determine whether to fire a warning.
We're changing how this check works in React 18. As a first step, this
refactors the logic into a single function. No behavior changes yet.

* Use IS_REACT_ACT_ENVIRONMENT to disable warnings

If `IS_REACT_ACT_ENVIRONMENT` is set to `false`, we will suppress
any `act` warnings. Otherwise, the behavior of `act` is the same as in
React 17: if `jest` is defined, it warns.

In concurrent mode, the plan is to remove the `jest` check and only warn
if `IS_REACT_ACT_ENVIRONMENT` is true. I have not implemented that
part yet.
2021-10-18 08:27:26 -07:00
Andrew Clark 7142d110b0 Bugfix: Nested useOpaqueIdentifier references (#22553)
* Handle render phase updates explicitly

We fire a warning in development if a component is updated during
the render phase (with the exception of local hook updates, which have
their own defined behavior).

Because it's not a supported React pattern, we don't have that many
tests that trigger this path. But it is meant to have reasonable
semantics when it does happen, so that if it accidentally ships to
production, the app doesn't crash unnecessarily. The behavior is not
super well-defined, though.

There are also some _internal_ React implementation details that
intentionally to rely on this behavior. Most prominently, selective
hydration and useOpaqueIdentifier.

I need to tweak the behavior of render phase updates slightly as part
of a fix for useOpaqueIdentifier. This shouldn't cause a user-facing
change in behavior outside of useOpaqueIdentifier, but it does require
that we explicitly model render phase updates.

* Bugfix: Nested useOpaqueIdentifier calls

Fixes an issue where multiple useOpaqueIdentifier hooks are upgraded
to client ids within the same render.

The way the upgrade works is that useOpaqueIdentifier schedules a
render phase update then throws an error to trigger React's error
recovery mechanism.

The normal error recovery mechanism is designed for errors that occur
as a result of interleaved mutations, so we usually only retry a single
time, synchronously, before giving up.

useOpaqueIdentifier is different because the error its throws when
upgrading is not caused by an interleaved mutation. Rather, it happens
when an ID is referenced for the first time inside a client-rendered
tree (i.e. sommething that wasn't part of the initial server render).
The fact that it relies on the error recovery mechanism is an
implementation detail. And a single recovery attempt may be
insufficient. For example, if a parent and a child component may
reference different ids, and both are mounted as a result of the same
client update, that will trigger two separate error recovery attempts.

Because render phase updates are not allowed when triggered from
userspace — we log a warning in developement to prevent them —
we can assume that if something does update during the render phase, it
is one of our "legit" implementation details like useOpaqueIdentifier.
So we can keep retrying until we succeed — up to a limit, to protect
against inifite loops. I chose 50 since that's the limit we use for
commit phase updates.
2021-10-13 15:44:36 -07:00
Andrew Clark a724a3b578 [RFC] Codemod invariant -> throw new Error (#22435)
* Hoist error codes import to module scope

When this code was written, the error codes map (`codes.json`) was
created on-the-fly, so we had to lazily require from inside the visitor.

Because `codes.json` is now checked into source, we can import it a
single time in module scope.

* Minify error constructors in production

We use a script to minify our error messages in production. Each message
is assigned an error code, defined in `scripts/error-codes/codes.json`.
Then our build script replaces the messages with a link to our
error decoder page, e.g. https://reactjs.org/docs/error-decoder.html/?invariant=92

This enables us to write helpful error messages without increasing the
bundle size.

Right now, the script only works for `invariant` calls. It does not work
if you throw an Error object. This is an old Facebookism that we don't
really need, other than the fact that our error minification script
relies on it.

So, I've updated the script to minify error constructors, too:

Input:
  Error(`A ${adj} message that contains ${noun}`);
Output:
  Error(formatProdErrorMessage(ERR_CODE, adj, noun));

It only works for constructors that are literally named Error, though we
could add support for other names, too.

As a next step, I will add a lint rule to enforce that errors written
this way must have a corresponding error code.

* Minify "no fallback UI specified" error in prod

This error message wasn't being minified because it doesn't use
invariant. The reason it didn't use invariant is because this particular
error is created without begin thrown — it doesn't need to be thrown
because it's located inside the error handling part of the runtime.

Now that the error minification script supports Error constructors, we
can minify it by assigning it a production error code in
`scripts/error-codes/codes.json`.

To support the use of Error constructors more generally, I will add a
lint rule that enforces each message has a corresponding error code.

* Lint rule to detect unminified errors

Adds a lint rule that detects when an Error constructor is used without
a corresponding production error code.

We already have this for `invariant`, but not for regular errors, i.e.
`throw new Error(msg)`. There's also nothing that enforces the use of
`invariant` besides convention.

There are some packages where we don't care to minify errors. These are
packages that run in environments where bundle size is not a concern,
like react-pg. I added an override in the ESLint config to ignore these.

* Temporarily add invariant codemod script

I'm adding this codemod to the repo temporarily, but I'll revert it
in the same PR. That way we don't have to check it in but it's still
accessible (via the PR) if we need it later.

* [Automated] Codemod invariant -> Error

This commit contains only automated changes:

npx jscodeshift -t scripts/codemod-invariant.js packages --ignore-pattern="node_modules/**/*"
yarn linc --fix
yarn prettier

I will do any manual touch ups in separate commits so they're easier
to review.

* Remove temporary codemod script

This reverts the codemod script and ESLint config I added temporarily
in order to perform the invariant codemod.

* Manual touch ups

A few manual changes I made after the codemod ran.

* Enable error code transform per package

Currently we're not consistent about which packages should have their
errors minified in production and which ones should.

This adds a field to the bundle configuration to control whether to
apply the transform. We should decide what the criteria is going
forward. I think it's probably a good idea to minify any package that
gets sent over the network. So yes to modules that run in the browser,
and no to modules that run on the server and during development only.
2021-09-30 12:01:28 -07:00
Joseph Savona 201af81b01 Release pooled cache reference in complete/unwind (#22464) 2021-09-29 18:49:52 -04:00
Andrew Clark d9fb383d6f Extract queueing logic into shared functions (#22452)
As a follow up to #22445, this extracts the queueing logic that is
shared between `dispatchSetState` and `dispatchReducerAction` into
separate functions. It likely doesn't save any bytes since these will
get inlined, anyway, but it does make the flow a bit easier to follow.
2021-09-28 12:05:44 -07:00
Brian Vaughn 9175f4d15e Scheduling Profiler: Show Suspense resource .displayName (#22451) 2021-09-28 11:29:33 -04:00
Joseph Savona 66388150ef Remove usereducer eager bailout (#22445)
* Fork dispatchAction for useState/useReducer

* Remove eager bailout from forked dispatchReducerAction, update tests

* Update eager reducer/state logic to handle state case only

* sync reconciler fork

* rename test

* test cases from #15198

* comments on new test cases

* comments on new test cases

* test case from #21419

* minor tweak to test name to kick CI
2021-09-27 16:25:10 -07:00
Andrew Clark d3e0869324 Make root.unmount() synchronous (#22444)
* Move flushSync warning to React DOM

When you call in `flushSync` from an effect, React fires a warning. I've
moved the implementation of this warning out of the reconciler and into
React DOM.

`flushSync` is a renderer API, not an isomorphic API, because it has
behavior that was designed specifically for the constraints of React
DOM. The equivalent API in a different renderer may not be the same.
For example, React Native has a different threading model than the
browser, so it might not make sense to expose a `flushSync` API to the
JavaScript thread.

* Make root.unmount() synchronous

When you unmount a root, the internal state that React stores on the
DOM node is immediately cleared. So, we should also synchronously
delete the React tree. You should be able to create a new root using
the same container.
2021-09-27 14:04:39 -07:00
Justin Grant c88fb49d37 Improve DEV errors if string coercion throws (Temporal.*, Symbol, etc.) (#22064)
* Revise ESLint rules for string coercion

Currently, react uses `'' + value` to coerce mixed values to strings.
This code will throw for Temporal objects or symbols.

To make string-coercion safer and to improve user-facing error messages,
This commit adds a new ESLint rule called `safe-string-coercion`.

This rule has two modes: a production mode and a non-production mode.
* If the `isProductionUserAppCode` option is true, then `'' + value`
  coercions are allowed (because they're faster, although they may
  throw) and `String(value)` coercions are disallowed. Exception:
  when building error messages or running DEV-only code in prod
  files, `String()` should be used because it won't throw.
* If the `isProductionUserAppCode` option is false, then `'' + value`
  coercions are disallowed (because they may throw, and in non-prod
  code it's not worth the risk) and `String(value)` are allowed.

Production mode is used for all files which will be bundled with
developers' userland apps. Non-prod mode is used for all other React
code: tests, DEV blocks, devtools extension, etc.

In production mode, in addiiton to flagging `String(value)` calls,
the rule will also flag `'' + value` or `value + ''` coercions that may
throw. The rule is smart enough to silence itself in the following
"will never throw" cases:
* When the coercion is wrapped in a `typeof` test that restricts to safe
  (non-symbol, non-object) types. Example:
    if (typeof value === 'string' || typeof value === 'number') {
      thisWontReport('' + value);
    }
* When what's being coerced is a unary function result, because unary
   functions never return an object or a symbol.
* When the coerced value is a commonly-used numeric identifier:
  `i`, `idx`, or `lineNumber`.
* When the statement immeidately before the coercion is a DEV-only
  call to a function from shared/CheckStringCoercion.js. This call is a
  no-op in production, but in DEV it will show a console error
  explaining the problem, then will throw right after a long explanatory
  code comment so that debugger users will have an idea what's going on.
  The check function call must be in the following format:
    if (__DEV__) {
      checkXxxxxStringCoercion(value);
    };

Manually disabling the rule is usually not necessary because almost all
prod use of the `'' + value` pattern falls into one of the categories
above. But in the rare cases where the rule isn't smart enough to detect
safe usage (e.g. when a coercion is inside a nested ternary operator),
manually disabling the rule will be needed.

The rule should also be manually disabled in prod error handling code
where `String(value)` should be used for coercions, because it'd be
bad to throw while building an error message or stack trace!

The prod and non-prod modes have differentiated error messages to
explain how to do a proper coercion in that mode.

If a production check call is needed but is missing or incorrect
(e.g. not in a DEV block or not immediately before the coercion), then
a context-sensitive error message will be reported so that developers
can figure out what's wrong and how to fix the problem.

Because string coercions are now handled by the `safe-string-coercion`
rule, the `no-primitive-constructor` rule no longer flags `String()`
usage. It still flags `new String(value)` because that usage is almost
always a bug.

* Add DEV-only string coercion check functions

This commit adds DEV-only functions to check whether coercing
values to strings using the `'' + value` pattern will throw. If it will
throw, these functions will:
1. Display a console error with a friendly error message describing
   the problem and the developer can fix it.
2. Perform the coercion, which will throw. Right before the line where
   the throwing happens, there's a long code comment that will help
   debugger users (or others looking at the exception call stack) figure
   out what happened and how to fix the problem.

One of these check functions should be called before all string coercion
of user-provided values, except when the the coercion is guaranteed not
to throw, e.g.
* if inside a typeof check like `if (typeof value === 'string')`
* if coercing the result of a unary function like `+value` or `value++`
* if coercing a variable named in a whitelist of numeric identifiers:
  `i`, `idx`, or `lineNumber`.

The new `safe-string-coercion` internal ESLint rule enforces that
these check functions are called when they are required.

Only use these check functions in production code that will be bundled
with user apps.  For non-prod code (and for production error-handling
code), use `String(value)` instead which may be a little slower but will
never throw.

* Add failing tests for string coercion

Added failing tests to verify:
* That input, select, and textarea elements with value and defaultValue
  set to Temporal-like objects which will throw when coerced to string
  using the `'' + value` pattern.
* That text elements will throw for Temporal-like objects
* That dangerouslySetInnerHTML will *not* throw for Temporal-like
  objects because this value is not cast to a string before passing to
  the DOM.
* That keys that are Temporal-like objects will throw

All tests above validate the friendly error messages thrown.

* Use `String(value)` for coercion in non-prod files

This commit switches non-production code from `'' + value` (which
throws for Temporal objects and symbols) to instead use `String(value)`
which won't throw for these or other future plus-phobic types.

"Non-produciton code" includes anything not bundled into user apps:
* Tests and test utilities. Note that I didn't change legacy React
  test fixtures because I assumed it was good for those files to
  act just like old React, including coercion behavior.
* Build scripts
* Dev tools package - In addition to switching to `String`, I also
  removed special-case code for coercing symbols which is now
  unnecessary.

* Add DEV-only string coercion checks to prod files

This commit adds DEV-only function calls to to check if string coercion
using `'' + value` will throw, which it will if the value is a Temporal
object or a symbol because those types can't be added with `+`.

If it will throw, then in DEV these checks will show a console error
to help the user undertsand what went wrong and how to fix the
problem. After emitting the console error, the check functions will
retry the coercion which will throw with a call stack that's easy (or
at least easier!) to troubleshoot because the exception happens right
after a long comment explaining the issue. So whether the user is in
a debugger, looking at the browser console, or viewing the in-browser
DEV call stack, it should be easy to understand and fix the problem.

In most cases, the safe-string-coercion ESLint rule is smart enough to
detect when a coercion is safe. But in rare cases (e.g. when a coercion
is inside a ternary) this rule will have to be manually disabled.

This commit also switches error-handling code to use `String(value)`
for coercion, because it's bad to crash when you're trying to build
an error message or a call stack!  Because `String()` is usually
disallowed by the `safe-string-coercion` ESLint rule in production
code, the rule must be disabled when `String()` is used.
2021-09-27 10:05:07 -07:00
Andrew Clark 05726d72cc [Fix] Errors should not "unsuspend" a transition (#22423)
If an error is thrown during a transition where we would have otherwise
suspended without showing a fallback (i.e. during a refresh), we should
still suspend.

The current behavior is that the error will force the fallback to
appear, even if it's completely unrelated to the component that errored,
which breaks the contract of `startTransition`.
2021-09-27 08:44:39 -07:00
Andrew Clark 04ccc01d96 Hydration errors should force a client render (#22416)
* Refactor throwException control flow

I'm about to add more branches to the Suspense-related logic in
`throwException`, so before I do, I split some of the steps into
separate functions so that later I can use them in multiple places.

This commit does not change any program behavior, only the control flow
surrounding existing code.

* Hydration errors should force a client render

If something errors during hydration, we should try rendering again
without hydrating.

We'll find the nearest Suspense boundary and force it to client render,
discarding the server-rendered content.
2021-09-24 10:47:19 -07:00
Andrew Clark 029fdcebbf root.hydrate -> root.isDehydrated (#22420)
I think this naming is a bit clearer. It means the root is currently
showing server rendered content that needs to be hydrated.

A dehydrated root is conceptually the same as what we call dehydrated
Suspense boundary, so this makes the naming of the root align with the
naming of subtrees.
2021-09-24 10:45:46 -07:00
Brian Vaughn af87f5a83d Scheduling Profiler marks should include thrown Errors (#22417)
The scheduling profiler markComponentRenderStopped method is supposed to be called when rendering finishes or when a value is thrown (Suspense or Error). Previously we were calling this in a Suspense-only path of `throwException`.

This PR updates the code to handle errors (or non-Thenables) thrown as well.

It also moves the mark logic the work loop `handleError` method, with Suspense/Error agnostic cleanup.
2021-09-24 12:32:37 -04:00
Andrew Clark 3a50d95573 Never attach ping listeners in legacy Suspense (#22407)
I noticed a weird branch where we attach a ping listener even in legacy
mode. It's weird because this shouldn't be necessary. Fallbacks always
synchronously commit in legacy mode, so pings never happen. (A "ping" is
when a suspended promise resolves before the fallback has committed.)

It took me a moment to remember why this case exists, but it's related
to React.lazy.

There's a special case where we suspend while reconciling the children
of a Suspense boundary's inner Offscreen wrapper fiber. This happens
when a React.lazy component is a direct child of a Suspense boundary.

Suspense boundaries are implemented as multiple fibers, but they are a
single conceptual unit. The legacy mode behavior where we pretend the
suspended fiber committed as `null` won't work, because in this case the
"suspended" fiber is the inner Offscreen wrapper.

Because the contents of the boundary haven't started rendering yet (i.e.
nothing in the tree has partially rendered) we can switch to the
regular, concurrent mode behavior: mark the boundary with ShouldCapture
and enter the unwind phase.

However, even though we're switching to the concurrent mode behavior, we
don't need to attach a ping listener. So I refactored the logic so that
it doesn't escape back into the regular path.

It's not really a big deal that we attach an unncessary ping listener,
since this case is so unusual. The motivation is not performance related
— it's to make the logic clearer, because I'm about to add another case
where we trigger a Suspense boundary without attaching a ping listener.
2021-09-23 11:07:38 -07:00
Andrew Clark 82c8fa90be Add back useMutableSource temporarily (#22396)
Recoil uses useMutableSource behind a flag. I thought this was fine
because Recoil isn't used in any concurrent roots, so the behavior
would be the same, but it turns out that it is used by concurrent
roots in a few places.

I'm not expecting it to be hard to migrate to useSyncExternalStore, but
to de-risk the change I'm going to roll it out gradually with a flag. In
the meantime, I've added back the useMutableSource API.
2021-09-21 20:38:24 -07:00