In https://github.com/facebook/react/pull/27472 I've removed broken
`useMemoCache` implementation and replaced it with a stub. It actually
produces errors when trying to inspect components, which are compiled
with Forget.
The main difference from the implementation in
https://github.com/facebook/react/pull/26696 is that we are using
corresponding `Fiber` here, which has patched `updateQueue` with
`memoCache`. Previously we would check it on a hook object, which
doesn't have `updateQueue`.
Tested on pages, which are using Forget and by inspecting elements,
which are transpiled with Forget.
Adds a second argument to useDeferredValue called initialValue:
```js
const value = useDeferredValue(finalValue, initialValue);
```
During the initial render of a component, useDeferredValue will return
initialValue. Once that render finishes, it will spawn an additional
render to switch to finalValue.
This same sequence should occur whenever the hook is hidden and revealed
again, i.e. by a Suspense or Activity, though this part is not yet
implemented.
When initialValue is not provided, useDeferredValue has no effect during
initial render, but during an update, it will remain on the previous
value, then spawn an additional render to switch to the new value. (This
is the same behavior that exists today.)
During SSR, initialValue is always used, if provided.
This feature is currently behind an experimental flag. We plan to ship
it in a non-breaking release.
## Summary
This PR cleans up `useMutableSource`. This has been blocked by a
remaining dependency internally at Meta, but that has now been deleted.
<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->
## How did you test this change?
```
yarn flow
yarn lint
yarn test --prod
```
<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
If you leave this empty, your PR will very likely be closed.
-->
useMemoCache wasn't previously supported in the DevTools, so any attempt
to inspect a component using the hook would result in a
`dispatcher.useMemoCache is not a function (it is undefined)` error.
- substr is Annex B
- substring silently flips its arguments if they're in the "wrong order", which is confusing
- slice is better than sliced bread (no pun intended) and also it works the same way on Arrays so there's less to remember
---
> I'd be down to just lint and enforce a single form just for the potential compression savings by using a repeated string.
_Originally posted by @sebmarkbage in https://github.com/facebook/react/pull/26663#discussion_r1170455401_
Added an explicit type to all $FlowFixMe suppressions to reduce
over-suppressions of new errors that might be caused on the same lines.
Also removes suppressions that aren't used (e.g. in a `@noflow` file as
they're purely misleading)
Test Plan:
yarn flow-ci
This enables the "exact_empty_objects" setting for Flow which makes
empty objects exact instead of building up the type as properties are
added in code below. This is in preparation to Flow 191 which makes this
the default and removes the config.
More about the change in the Flow blog
[here](https://medium.com/flow-type/improved-handling-of-the-empty-object-in-flow-ead91887e40c).
This setting is an incremental path to the next Flow version enforcing
type annotations on most functions (except some inline callbacks).
Used
```
node_modules/.bin/flow codemod annotate-functions-and-classes --write .
```
to add a majority of the types with some hand cleanup when for large
inferred objects that should just be `Fiber` or weird constructs
including `any`.
Suppressed the remaining issues.
Builds on #25918
* Facebook -> Meta in copyright
rg --files | xargs sed -i 's#Copyright (c) Facebook, Inc. and its affiliates.#Copyright (c) Meta Platforms, Inc. and affiliates.#g'
* Manual tweaks
* Missing Hooks
* Remove www forks. These can use __SECRET... instead.
* Move cache to separate dispatcher
These will be available in more contexts than just render.
* [DevTools] fix useDeferredValue to match reconciler change
* fixup
* update test to catch original issue
* fix lint
* add safer tests for other composite hooks
* add transition name to startTransition
Add a transitionName to start transition, store the transition start time and name in the batch config, and pass it to the root on render
* Transition Tracing Types and Consts
* Root begin work
The root operates as a tracing marker that has all transitions on it. This PR only tested the root with one transition so far
- Store transitions in memoizedState. Do this in updateHostRoot AND attemptEarlyBailoutIfNoScheduledUpdate. We need to do this in the latter part because even if the root itself doesn't have an update, it could still have new transitions in its transitionLanes map that we need to process.
* Transition Tracing commit phase
- adds a module scoped pending transition callbacks object that contains all transition callbacks that have not yet been processed. This contains all callbacks before the next paint occurs.
- Add code in the mutation phase to:
* For the root, if there are transitions that were initialized during this commit in the root transition lanes map, add a transition start call to the pending transition callbacks object. Then, remove the transitions from the root transition lanes map.
* For roots, in the commit phase, add a transition complete call
We add this code in the mutation phase because we can't add it to the passive phase because then the paint might have occurred before we even know which callbacks to call
* Process Callbacks after paint
At the end of the commit phase, call scheduleTransitionCallbacks to schedule all pending transition callbacks to be called after paint. Then clear the callbacks
* Remove object-assign polyfill
We really rely on a more modern environment where this is typically
polyfilled anyway and we don't officially support IE with more extensive
polyfilling anyway. So all environments should have the native version
by now.
* Use shared/assign instead of Object.assign in code
This is so that we have one cached local instance in the bundle.
Ideally we should have a compile do this for us but we already follow
this pattern with hasOwnProperty, isArray, Object.is etc.
* Transform Object.assign to now use shared/assign
We need this to use the shared instance when Object.spread is used.
* 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.
* 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.
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.
Adds a third argument called `getServerSnapshot`.
On the server, React calls this one instead of the normal `getSnapshot`.
We also call it during hydration.
So it represents the snapshot that is used to generate the initial,
server-rendered HTML. The purpose is to avoid server-client mismatches.
What we render during hydration needs to match up exactly with what we
render on the server.
The pattern is for the server to send down a serialized copy of the
store that was used to generate the initial HTML. On the client, React
will call either `getSnapshot` or `getServerSnapshot` on the client as
appropriate, depending on whether it's currently hydrating.
The argument is optional for fully client rendered use cases. If the
user does attempt to omit `getServerSnapshot`, and the hook is called
on the server, React will abort that subtree on the server and
revert to client rendering, up to the nearest Suspense boundary.
For the userspace shim, we will need to use a heuristic (canUseDOM)
to determine whether we are in a server environment. I'll do that in
a follow up.
* [useSyncExternalStore] Remove extra hook object
Because we already track `getSnapshot` and `value` on the store
instance, we don't need to also track them as effect dependencies. And
because the effect doesn't require any clean-up, we don't need to track
a `destroy` function.
So, we don't need to store any additional state for this effect. We can
call `pushEffect` directly, and only during renders where something
has changed.
This saves some memory, but my main motivation is because I plan to use
this same logic to schedule a pre-commit consistency check. (See the
inline comments for more details.)
* Split shouldTimeSlice into two separate functions
Lanes that are blocking (SyncLane, and DefaultLane inside a blocking-
by-default root) are always blocking for a given root. Whereas expired
lanes can expire while the render phase is already in progress.
I want to check if a lane is blocking without checking whether it
expired, so I split `shouldTimeSlice` into two separate functions.
I'll use this in the next step.
* Check for store mutations before commit
When a store is read for the first time, or when `subscribe` or
`getSnapshot` changes, during a concurrent render, we have to check
at the end of the render phase whether the store was mutated by
an concurrent event.
In the userspace shim, we perform this check in a layout effect, and
patch up any inconsistencies by scheduling another render + commit.
However, even though we patch them up in the next render, the parent
layout effects that fire in the original render will still observe an
inconsistent tree.
In the native implementation, we can instead check for inconsistencies
right after the root is completed, before entering the commit phase. If
we do detect a mutaiton, we can discard the tree and re-render before
firing any effects. The re-render is synchronous to block further
concurrent mutations (which is also what we do to recover from tearing
bugs that result in an error). After the synchronous re-render, we can
assume the tree the tree is consistent and continue with the normal
algorithm for finishing a completed root (i.e. either suspend
or commit).
The result is that layout effects will always observe a consistent tree.
Adds support for useSyncExternalStore to react-debug-tools, which in
turn adds support for React Devtools.
Test plan: I added a test to ReactHooksInspectionIntegration, based on
existing one for useMutableSource.
We added this unstable feature a few years ago, as a way to opt out of
context updates, but it didn't prove useful in practice.
We have other proposals for how to address the same problem, like
context selectors.
Since it was prefixed with `unstable_`, we should be able to remove it
without consequence. The hook API already warned if you used it.
Even if someone is using it somewhere, it's meant to be an optimization
only, so if they are using the API properly, it should not have any
semantic impact.
* Remove react/unstable_cache
We're probably going to make it available via the dispatcher. Let's remove this for now.
* Add readContext() to the dispatcher
On the server, it will be per-request.
On the client, there will be some way to shadow it.
For now, I provide it on the server, and throw on the client.
* Use readContext() from react-fetch
This makes it work on the server (but not on the client until we implement it there.)
Updated the test to use Server Components. Now it passes.
* Fixture: Add fetch from a Server Component
* readCache -> getCacheForType<T>
* Add React.unstable_getCacheForType
* Add a feature flag
* Fix Flow
* Add react-suspense-test-utils and port tests
* Remove extra Map lookup
* Unroll async/await because build system
* Add some error coverage and retry
* Add unstable_getCacheForType to Flight entry
* Remove Blocks
* Remove Flight Server Runtime
There's no need for this now that the JSResource is part of the bundler
protocol. Might need something for Webpack plugin specifically later.
* Devtools
And `useDeferredValue`.
The options were already disabled in previous commits, so this doesn't
change any behavior. I upated type signatures and cleaned up the hook
implementation a bit — no longer have to wrap the `start` method with
`useCallback`, because its only remaining dependency is a `setState`
method, which never changes. Instead, we can store the `start` method
on a ref.
Now that the options in SuspenseConfig are no longer supported, the
only thing we use it for is to track whether an update is part of
a transition.
I've renamed `ReactCurrentBatchConfig.suspense` to
`ReactCurrentBatchConfig.transition`, and changed the type to a number.
The number is always either 0 or 1. I could have made it a boolean;
however, most likely this will eventually be either a Lane or an
incrementing identifier.
The `withSuspenseConfig` export still exists until we've removed
all the callers from www.
* Add "unstbale_" prefix to mutable source APIs
* DebugHooks no longer calls useMutableSource() on init
This was causing an observable behavioral difference between experimental DEV and PROD builds.
We don't initialize stack position for other composite hooks (e.g. useDeferredValue, useTransition, useOpaqueIdentifier). If we did, it would cause the same obesrvable behavioral difference.
Some of our internal reconciler types have leaked into other packages.
Usually, these types are treated as opaque; we don't read and write
to its fields. This is good.
However, the type is often passed back to a reconciler method. For
example, React DOM creates a FiberRoot with `createContainer`, then
passes that root to `updateContainer`. It doesn't do anything with the
root except pass it through, but because `updateContainer` expects a
full FiberRoot, React DOM is still coupled to all its fields.
I don't know if there's an idiomatic way to handle this in Flow. Opaque
types are simlar, but those only work within a single file. AFAIK,
there's no way to use a package as the boundary for opaqueness.
The immediate problem this presents is that the reconciler refactor will
involve changes to our internal data structures. I don't want to have to
fork every single package that happens to pass through a Fiber or
FiberRoot, or access any one of its fields. So my current plan is to
share the same Flow type across both forks. The shared type will be a
superset of each implementation's type, e.g. Fiber will have both an
`expirationTime` field and a `lanes` field. The implementations will
diverge, but not the types.
To do this, I lifted the type definitions into a separate module.
* Add useOpaqueIdentifier Hook
We currently use unique IDs in a lot of places. Examples are:
* `<label for="ID">`
* `aria-labelledby`
This can cause some issues:
1. If we server side render and then hydrate, this could cause an
hydration ID mismatch
2. If we server side render one part of the page and client side
render another part of the page, the ID for one part could be
different than the ID for another part even though they are
supposed to be the same
3. If we conditionally render something with an ID , this might also
cause an ID mismatch because the ID will be different on other
parts of the page
This PR creates a new hook `useUniqueId` that generates a different
unique ID based on whether the hook was called on the server or client.
If the hook is called during hydration, it generates an opaque object
that will rerender the hook so that the IDs match.
Co-authored-by: Andrew Clark <git@andrewclark.io>