SIGN IN SIGN UP
facebook / react UNCLAIMED

The library for web and native user interfaces.

0 0 1173 JavaScript
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
Convert current build system to Rollup and adopt flat bundles (#9327) * WIP * fbjs support * WIP * dev/prod mode WIP * More WIP * builds a cjs bundle * adding forwarding modules * more progress on forwarding modules and FB config * improved how certain modules get inlined for fb and cjs * more forwarding modules * added comments to the module aliasing code * made ReactPerf and ReactTestUtils bundle again * Use -core suffix for all bundles This makes it easier to override things in www. * Add a lazy shim for ReactPerf This prevents a circular dependency between ReactGKJSModule and ReactDOM * Fix forwarding module for ReactCurrentOwner * Revert "Add a lazy shim for ReactPerf" This reverts commit 723b402c07116a70ce8ff1e43a1f4d92052e8f43. * Rename -core suffix to -fb for clarity * Change forwarding modules to import from -fb This is another, more direct fix for ReactPerf circular dependency * should fix fb and cjs bundles for ReactCurrentOwner * added provides module for ReactCurrentOwner * should improve console output * fixed typo with argument passing on functon call * Revert "should improve console output" This breaks the FB bundles. This reverts commit 65f11ee64f678c387cb3cfef9a8b28b89a6272b9. * Work around internal FB transform require() issue * moved ReactInstanceMap out of React and into ReactDOM and ReactDOMFiber * Expose more internal modules to www * Add missing modules to Stack ReactDOM to fix UFI * Fix onlyChild module * improved the build tool * Add a rollup npm script * Rename ReactDOM-fb to ReactDOMStack-fb * Fix circular dependencies now that ReactDOM-fb is a GK switch * Revert "Work around internal FB transform require() issue" This reverts commit 0a50b6a90bffc59f8f5416ef36000b5e3a44d253. * Bump rollup-plugin-commonjs to include a fix for rollup/rollup-plugin-commonjs#176 * Add more forwarding modules that are used on www * Add even more forwarding modules that are used on www * Add DOMProperty to hidden exports * Externalize feature flags This lets www specify them dynamically. * Remove forwarding modules with implementations Instead I'm adding them to react-fb in my diff. * Add all injection necessary for error logging * Add missing forwarding module (oops) * Add ReactART builds * Add ReactDOMServer bundle * Fix UMD build of ReactDOMFiber * Work in progress: start adding ReactNative bundle * tidied up the options for bundles, so they can define what types they output and exclude * Add a working RN build * further improved and tidied up build process * improved how bundles are built by exposing externals and making the process less "magical", also tidied up code and added more comments * better handling of bundling ReactCurrentOwner and accessing it from renderer modules * added NODE_DEV and NODE_PROD * added NPM package creation and copying into build chain * Improved UMD bundles, added better fixture testing and doc plus prod builds * updated internal modules (WIP) * removed all react/lib/* dependencies from appearing in bundles created on build * added react-test-renderer bundles * renamed bundles and paths * fixed fixture path changes * added extract-errors support * added extractErrors warning * moved shims to shims directory in rollup scripts * changed pathing to use build rather than build/rollup * updated release doc to reflect some rollup changes * Updated ReactNative findNodeHandle() to handle number case (#9238) * Add dynamic injection to ReactErrorUtils (#9246) * Fix ReactErrorUtils injection (#9247) * Fix Haste name * Move files around * More descriptive filenames * Add missing ReactErrorUtils shim * Tweak reactComponentExpect to make it standalone-ish in www * Unflowify shims * facebook-www shims now get copied over correctly to build * removed unnecessary resolve * building facebook-www/build is now all sync to prevent IO issues plus handles extra facebook-www src assets * removed react-native-renderer package and made build make a react-native build dir instead * 😭😭😭 * Add more SSR unit tests for elements and children. (#9221) * Adding more SSR unit tests for elements and children. * Some of my SSR tests were testing for react-text and react-empty elements that no longer exist in Fiber. Fixed the tests so that they expect correct markup in Fiber. * Tweaked some test names after @gaearon review comment https://github.com/facebook/react/pull/9221#discussion_r107045673 . Also realized that one of the tests was essentially a direct copy of another, so deleted it. * Responding to code review https://github.com/facebook/react/pull/9221#pullrequestreview-28996315 . Thanks @spicyj! * ReactElementValidator uses temporary ReactNative View propTypes getter (#9256) * Updating packages for 16.0.0-alpha.6 release * Revert "😭😭😭" This reverts commit 7dba33b2cfc67246881f6d57633a80e628ea05ec. * Work around Jest issue with CurrentOwner shared state in www * updated error codes * splits FB into FB_DEV and FB_PROD * Remove deps on specific builds from shims * should no longer mangle FB_PROD output * Added init() dev block to ReactTestUtils * added shims for DEV only code so it does not get included in prod bundles * added a __DEV__ wrapping code to FB_DEV * added __DEV__ flag behind a footer/header * Use right haste names * keeps comments in prod * added external babel helpers plugin * fixed fixtures and updated cjs/umd paths * Fixes Jest so it run tests correctly * fixed an issue with stubbed modules not properly being replaced due to greedy replacement * added a WIP solution for ReactCurrentOwner on FB DEV * adds a FB_TEST bundle * allows both ReactCurrentOwner and react/lib/ReactCurrentOwner * adds -test to provides module name * Remove TEST env * Ensure requires stay at the top * added basic mangle support (disbaled by default) * per bundle property mangling added * moved around plugin order to try and fix deadcode requires as per https://github.com/rollup/rollup/issues/855 * Fix flow issues * removed gulp and grunt and moved tasks to standalone node script * configured circleci to use new paths * Fix lint * removed gulp-extract-errors * added test_build.sh back in * added missing newline to flow.js * fixed test coverage command * changed permissions on test_build.sh * fixed test_html_generations.sh * temp removed html render test * removed the warning output from test_build, the build should do this instead * fixed test_build * fixed broken npm script * Remove unused ViewportMetrics shim * better error output * updated circleci to node 7 for async/await * Fixes * removed coverage test from circleci run * circleci run tets * removed build from circlci * made a dedicated jest script in a new process * moved order around of circlci tasks * changing path to jest in more circleci tests * re-enabled code coverage * Add file header to prod bundles * Remove react-dom/server.js (WIP: decide on the plan) * Only UMD bundles need version header * Merge with master * disabled const evaluation by uglify for <script></script> string literal * deal with ART modules for UMD bundles * improved how bundle output gets printed * fixed filesize difference reporting * added filesize dep * Update yarn lockfile for some reason * now compares against the last run branch built on * added react-dom-server * removed un-needed comment * results only get saved on full builds * moved the rollup sized plugin into a plugins directory * added a missing commonjs() * fixed missing ignore * Hack around to fix RN bundle * Partially fix RN bundles * added react-art bundle and a fixture for it * Point UMD bundle to Fiber and add EventPluginHub to exported internals * Make it build on Node 4 * fixed eslint error with resolve being defined in outer scope * Tweak how build results are calculated and stored * Tweak fixtures build to work on Node 4 * Include LICENSE/PATENTS and fix up package.json files * Add Node bundle for react-test-renderer * Revert "Hack around to fix RN bundle" We'll do this later. This reverts commit 59445a625962d7be4c7c3e98defc8a31f8761ec1. * Revert more RN changes We'll do them separately later * Revert more unintentional changes * Revert changes to error codes * Add accidentally deleted RN externals * added RN_DEV/RN_PROD bundles * fixed typo where RN_DEV and RN_PROD were the wrong way around * Delete/ignore fixture build outputs * Format scripts/ with Prettier * tidied up the Rollup build process and split functions into various different files to improve readability * Copy folder before files * updated yarn.lock * updated results and yarn dependencies to the latest versions
2017-04-05 16:47:29 +01:00
* @flow
*/
import type {LazyComponent} from 'react/src/ReactLazy';
Switch <Context> to mean <Context.Provider> (#28226) Previously, `<Context>` was equivalent to `<Context.Consumer>`. However, since the introduction of Hooks, the `<Context.Consumer>` API is rarely used. The goal here is to make the common case cleaner: ```js const ThemeContext = createContext('light') function App() { return ( <ThemeContext value="dark"> ... </ThemeContext> ) } function Button() { const theme = use(ThemeContext) // ... } ``` This is technically a breaking change, but we've been warning about rendering `<Context>` directly for several years by now, so it's unlikely much code in the wild depends on the old behavior. [Proof that it warns today (check console).](https://codesandbox.io/p/sandbox/peaceful-nobel-pdxtfl) --- **The relevant commit is 5696782b428a5ace96e66c1857e13249b6c07958.** It switches `createContext` implementation so that `Context.Provider === Context`. The main assumption that changed is that a Provider's fiber type is now the context itself (rather than an intermediate object). Whereas a Consumer's fiber type is now always an intermediate object (rather than it being sometimes the context itself and sometimes an intermediate object). My methodology was to start with the relevant symbols, work tags, and types, and work my way backwards to all usages. This might break tooling that depends on inspecting React's internal fields. I've added DevTools support in the second commit. This didn't need explicit versioning—the structure tells us enough.
2024-02-13 15:04:49 +00:00
import type {ReactContext, ReactConsumerType} from 'shared/ReactTypes';
Accept promise as element type (#13397) * Accept promise as element type On the initial render, the element will suspend as if a promise were thrown from inside the body of the unresolved component. Siblings should continue rendering and if the parent is a Placeholder, the promise should be captured by that Placeholder. When the promise resolves, rendering resumes. If the resolved value has a `default` property, it is assumed to be the default export of an ES module, and we use that as the component type. If it does not have a `default` property, we use the resolved value itself. The resolved value is stored as an expando on the promise/thenable. * Use special types of work for lazy components Because reconciliation is a hot path, this adds ClassComponentLazy, FunctionalComponentLazy, and ForwardRefLazy as special types of work. The other types are not supported, but wouldn't be placed into a separate module regardless. * Resolve defaultProps for lazy types * Remove some calls to isContextProvider isContextProvider checks the fiber tag, but it's typically called after we've already refined the type of work. We should get rid of it. I removed some of them in the previous commit, and deleted a few more in this one. I left a few behind because the remaining ones would require additional refactoring that feels outside the scope of this PR. * Remove getLazyComponentTypeIfResolved * Return baseProps instead of null The caller compares the result to baseProps to see if anything changed. * Avoid redundant checks by inlining getFiberTagFromObjectType * Move tag resolution to ReactFiber module * Pass next props to update* functions We should do this with all types of work in the future. * Refine component type before pushing/popping context Removes unnecessary checks. * Replace all occurrences of _reactResult with helper * Move shared thenable logic to `shared` package * Check type of wrapper object before resolving to `default` export * Return resolved tag instead of reassigning
2018-08-16 09:21:59 -07:00
import {
REACT_CONTEXT_TYPE,
Switch <Context> to mean <Context.Provider> (#28226) Previously, `<Context>` was equivalent to `<Context.Consumer>`. However, since the introduction of Hooks, the `<Context.Consumer>` API is rarely used. The goal here is to make the common case cleaner: ```js const ThemeContext = createContext('light') function App() { return ( <ThemeContext value="dark"> ... </ThemeContext> ) } function Button() { const theme = use(ThemeContext) // ... } ``` This is technically a breaking change, but we've been warning about rendering `<Context>` directly for several years by now, so it's unlikely much code in the wild depends on the old behavior. [Proof that it warns today (check console).](https://codesandbox.io/p/sandbox/peaceful-nobel-pdxtfl) --- **The relevant commit is 5696782b428a5ace96e66c1857e13249b6c07958.** It switches `createContext` implementation so that `Context.Provider === Context`. The main assumption that changed is that a Provider's fiber type is now the context itself (rather than an intermediate object). Whereas a Consumer's fiber type is now always an intermediate object (rather than it being sometimes the context itself and sometimes an intermediate object). My methodology was to start with the relevant symbols, work tags, and types, and work my way backwards to all usages. This might break tooling that depends on inspecting React's internal fields. I've added DevTools support in the second commit. This didn't need explicit versioning—the structure tells us enough.
2024-02-13 15:04:49 +00:00
REACT_CONSUMER_TYPE,
REACT_FORWARD_REF_TYPE,
REACT_FRAGMENT_TYPE,
REACT_PORTAL_TYPE,
2018-10-20 12:46:23 -04:00
REACT_MEMO_TYPE,
REACT_PROFILER_TYPE,
REACT_STRICT_MODE_TYPE,
REACT_SUSPENSE_TYPE,
REACT_SUSPENSE_LIST_TYPE,
REACT_LAZY_TYPE,
REACT_TRACING_MARKER_TYPE,
Add <ViewTransition> Component (#31975) This will provide the opt-in for using [View Transitions](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API) in React. View Transitions only trigger for async updates like `startTransition`, `useDeferredValue`, Actions or `<Suspense>` revealing from fallback to content. Synchronous updates provide an opt-out but also guarantee that they commit immediately which View Transitions can't. There's no need to opt-in to View Transitions at the "cause" side like event handlers or actions. They don't know what UI will change and whether that has an animated transition described. Conceptually the `<ViewTransition>` component is like a DOM fragment that transitions its children in its own isolate/snapshot. The API works by wrapping a DOM node or inner component: ```js import {ViewTransition} from 'react'; <ViewTransition><Component /></ViewTransition> ``` The default is `name="auto"` which will automatically assign a `view-transition-name` to the inner DOM node. That way you can add a View Transition to a Component without controlling its DOM nodes styling otherwise. A difference between this and the browser's built-in `view-transition-name: auto` is that switching the DOM nodes within the `<ViewTransition>` component preserves the same name so this example cross-fades between the DOM nodes instead of causing an exit and enter: ```js <ViewTransition>{condition ? <ComponentA /> : <ComponentB />}</ViewTransition> ``` This becomes especially useful with `<Suspense>` as this example cross-fades between Skeleton and Content: ```js <ViewTransition> <Suspense fallback={<Skeleton />}> <Content /> </Suspense> </ViewTransition> ``` Where as this example triggers an exit of the Skeleton and an enter of the Content: ```js <Suspense fallback={<ViewTransition><Skeleton /></ViewTransition>}> <ViewTransition><Content /></ViewTransition> </Suspense> ``` Managing instances and keys becomes extra important. You can also specify an explicit `name` property for example for animating the same conceptual item from one page onto another. However, best practices is to property namespace these since they can easily collide. It's also useful to add an `id` to it if available. ```js <ViewTransition name="my-shared-view"> ``` The model in general is the same as plain `view-transition-name` except React manages a set of heuristics for when to apply it. A problem with the naive View Transitions model is that it overly opts in every boundary that *might* transition into transitioning. This is leads to unfortunate effects like things floating around when unrelated updates happen. This leads the whole document to animate which means that nothing is clickable in the meantime. It makes it not useful for smaller and more local transitions. Best practice is to add `view-transition-name` only right before you're about to need to animate the thing. This is tricky to manage globally on complex apps and is not compositional. Instead we let React manage when a `<ViewTransition>` "activates" and add/remove the `view-transition-name`. This is also when React calls `startViewTransition` behind the scenes while it mutates the DOM. I've come up with a number of heuristics that I think will make a lot easier to coordinate this. The principle is that only if something that updates that particular boundary do we activate it. I hope that one day maybe browsers will have something like these built-in and we can remove our implementation. A `<ViewTransition>` only activates if: - If a mounted Component renders a `<ViewTransition>` within it outside the first DOM node, and it is within the viewport, then that ViewTransition activates as an "enter" animation. This avoids inner "enter" animations trigger when the parent mounts. - If an unmounted Component had a `<ViewTransition>` within it outside the first DOM node, and it was within the viewport, then that ViewTransition activates as an "exit" animation. This avoids inner "exit" animations triggering when the parent unmounts. - If an explicitly named `<ViewTransition name="...">` is deep within an unmounted tree and one with the same name appears in a mounted tree at the same time, then both are activated as a pair, but only if they're both in the viewport. This avoids these triggering "enter" or "exit" animations when going between parents that don't have a pair. - If an already mounted `<ViewTransition>` is visible and a DOM mutation, that might affect how it's painted, happens within its children but outside any nested `<ViewTransition>`. This allows it to "cross-fade" between its updates. - If an already mounted `<ViewTransition>` resizes or moves as the result of direct DOM nodes siblings changing or moving around. This allows insertion, deletion and reorders into a list to animate all children. It is only within one DOM node though, to avoid unrelated changes in the parent to trigger this. If an item is outside the viewport before and after, then it's skipped to avoid things flying across the screen. - If a `<ViewTransition>` boundary changes size, due to a DOM mutation within it, then the parent activates (or the root document if there are no more parents). This ensures that the container can cross-fade to avoid abrupt relayout. This can be avoided by using absolutely positioned children. When this can avoid bubbling to the root document, whatever is not animating is still responsive to clicks during the transition. Conceptually each DOM node has its own default that activates the parent `<ViewTransition>` or no transition if the parent is the root. That means that if you add a DOM node like `<div><ViewTransition><Component /></ViewTransition></div>` this won't trigger an "enter" animation since it was the div that was added, not the ViewTransition. Instead, it might cause a cross-fade of the parent ViewTransition or no transition if it had no parent. This ensures that only explicit boundaries perform coarse animations instead of every single node which is really the benefit of the View Transitions model. This ends up working out well for simple cases like switching between two pages immediately while transitioning one floating item that appears on both pages. Because only the floating item transitions by default. Note that it's possible to add manual `view-transition-name` with CSS or `style={{ viewTransitionName: 'auto' }}` that always transitions as long as something else has a `<ViewTransition>` that activates. For example a `<ViewTransition>` can wrap a whole page for a cross-fade but inside of it an explicit name can be added to something to ensure it animates as a move when something relates else changes its layout. Instead of just cross-fading it along with the Page which would be the default. There's more PRs coming with some optimizations, fixes and expanded APIs. This first PR explores the above core heuristic. --------- Co-authored-by: Sebastian "Sebbie" Silbermann <silbermann.sebastian@gmail.com>
2025-01-08 12:11:18 -05:00
REACT_VIEW_TRANSITION_TYPE,
REACT_ACTIVITY_TYPE,
} from 'shared/ReactSymbols';
Switch <Context> to mean <Context.Provider> (#28226) Previously, `<Context>` was equivalent to `<Context.Consumer>`. However, since the introduction of Hooks, the `<Context.Consumer>` API is rarely used. The goal here is to make the common case cleaner: ```js const ThemeContext = createContext('light') function App() { return ( <ThemeContext value="dark"> ... </ThemeContext> ) } function Button() { const theme = use(ThemeContext) // ... } ``` This is technically a breaking change, but we've been warning about rendering `<Context>` directly for several years by now, so it's unlikely much code in the wild depends on the old behavior. [Proof that it warns today (check console).](https://codesandbox.io/p/sandbox/peaceful-nobel-pdxtfl) --- **The relevant commit is 5696782b428a5ace96e66c1857e13249b6c07958.** It switches `createContext` implementation so that `Context.Provider === Context`. The main assumption that changed is that a Provider's fiber type is now the context itself (rather than an intermediate object). Whereas a Consumer's fiber type is now always an intermediate object (rather than it being sometimes the context itself and sometimes an intermediate object). My methodology was to start with the relevant symbols, work tags, and types, and work my way backwards to all usages. This might break tooling that depends on inspecting React's internal fields. I've added DevTools support in the second commit. This didn't need explicit versioning—the structure tells us enough.
2024-02-13 15:04:49 +00:00
import {
enableTransitionTracing,
Add <ViewTransition> Component (#31975) This will provide the opt-in for using [View Transitions](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API) in React. View Transitions only trigger for async updates like `startTransition`, `useDeferredValue`, Actions or `<Suspense>` revealing from fallback to content. Synchronous updates provide an opt-out but also guarantee that they commit immediately which View Transitions can't. There's no need to opt-in to View Transitions at the "cause" side like event handlers or actions. They don't know what UI will change and whether that has an animated transition described. Conceptually the `<ViewTransition>` component is like a DOM fragment that transitions its children in its own isolate/snapshot. The API works by wrapping a DOM node or inner component: ```js import {ViewTransition} from 'react'; <ViewTransition><Component /></ViewTransition> ``` The default is `name="auto"` which will automatically assign a `view-transition-name` to the inner DOM node. That way you can add a View Transition to a Component without controlling its DOM nodes styling otherwise. A difference between this and the browser's built-in `view-transition-name: auto` is that switching the DOM nodes within the `<ViewTransition>` component preserves the same name so this example cross-fades between the DOM nodes instead of causing an exit and enter: ```js <ViewTransition>{condition ? <ComponentA /> : <ComponentB />}</ViewTransition> ``` This becomes especially useful with `<Suspense>` as this example cross-fades between Skeleton and Content: ```js <ViewTransition> <Suspense fallback={<Skeleton />}> <Content /> </Suspense> </ViewTransition> ``` Where as this example triggers an exit of the Skeleton and an enter of the Content: ```js <Suspense fallback={<ViewTransition><Skeleton /></ViewTransition>}> <ViewTransition><Content /></ViewTransition> </Suspense> ``` Managing instances and keys becomes extra important. You can also specify an explicit `name` property for example for animating the same conceptual item from one page onto another. However, best practices is to property namespace these since they can easily collide. It's also useful to add an `id` to it if available. ```js <ViewTransition name="my-shared-view"> ``` The model in general is the same as plain `view-transition-name` except React manages a set of heuristics for when to apply it. A problem with the naive View Transitions model is that it overly opts in every boundary that *might* transition into transitioning. This is leads to unfortunate effects like things floating around when unrelated updates happen. This leads the whole document to animate which means that nothing is clickable in the meantime. It makes it not useful for smaller and more local transitions. Best practice is to add `view-transition-name` only right before you're about to need to animate the thing. This is tricky to manage globally on complex apps and is not compositional. Instead we let React manage when a `<ViewTransition>` "activates" and add/remove the `view-transition-name`. This is also when React calls `startViewTransition` behind the scenes while it mutates the DOM. I've come up with a number of heuristics that I think will make a lot easier to coordinate this. The principle is that only if something that updates that particular boundary do we activate it. I hope that one day maybe browsers will have something like these built-in and we can remove our implementation. A `<ViewTransition>` only activates if: - If a mounted Component renders a `<ViewTransition>` within it outside the first DOM node, and it is within the viewport, then that ViewTransition activates as an "enter" animation. This avoids inner "enter" animations trigger when the parent mounts. - If an unmounted Component had a `<ViewTransition>` within it outside the first DOM node, and it was within the viewport, then that ViewTransition activates as an "exit" animation. This avoids inner "exit" animations triggering when the parent unmounts. - If an explicitly named `<ViewTransition name="...">` is deep within an unmounted tree and one with the same name appears in a mounted tree at the same time, then both are activated as a pair, but only if they're both in the viewport. This avoids these triggering "enter" or "exit" animations when going between parents that don't have a pair. - If an already mounted `<ViewTransition>` is visible and a DOM mutation, that might affect how it's painted, happens within its children but outside any nested `<ViewTransition>`. This allows it to "cross-fade" between its updates. - If an already mounted `<ViewTransition>` resizes or moves as the result of direct DOM nodes siblings changing or moving around. This allows insertion, deletion and reorders into a list to animate all children. It is only within one DOM node though, to avoid unrelated changes in the parent to trigger this. If an item is outside the viewport before and after, then it's skipped to avoid things flying across the screen. - If a `<ViewTransition>` boundary changes size, due to a DOM mutation within it, then the parent activates (or the root document if there are no more parents). This ensures that the container can cross-fade to avoid abrupt relayout. This can be avoided by using absolutely positioned children. When this can avoid bubbling to the root document, whatever is not animating is still responsive to clicks during the transition. Conceptually each DOM node has its own default that activates the parent `<ViewTransition>` or no transition if the parent is the root. That means that if you add a DOM node like `<div><ViewTransition><Component /></ViewTransition></div>` this won't trigger an "enter" animation since it was the div that was added, not the ViewTransition. Instead, it might cause a cross-fade of the parent ViewTransition or no transition if it had no parent. This ensures that only explicit boundaries perform coarse animations instead of every single node which is really the benefit of the View Transitions model. This ends up working out well for simple cases like switching between two pages immediately while transitioning one floating item that appears on both pages. Because only the floating item transitions by default. Note that it's possible to add manual `view-transition-name` with CSS or `style={{ viewTransitionName: 'auto' }}` that always transitions as long as something else has a `<ViewTransition>` that activates. For example a `<ViewTransition>` can wrap a whole page for a cross-fade but inside of it an explicit name can be added to something to ensure it animates as a move when something relates else changes its layout. Instead of just cross-fading it along with the Page which would be the default. There's more PRs coming with some optimizations, fixes and expanded APIs. This first PR explores the above core heuristic. --------- Co-authored-by: Sebastian "Sebbie" Silbermann <silbermann.sebastian@gmail.com>
2025-01-08 12:11:18 -05:00
enableViewTransition,
Switch <Context> to mean <Context.Provider> (#28226) Previously, `<Context>` was equivalent to `<Context.Consumer>`. However, since the introduction of Hooks, the `<Context.Consumer>` API is rarely used. The goal here is to make the common case cleaner: ```js const ThemeContext = createContext('light') function App() { return ( <ThemeContext value="dark"> ... </ThemeContext> ) } function Button() { const theme = use(ThemeContext) // ... } ``` This is technically a breaking change, but we've been warning about rendering `<Context>` directly for several years by now, so it's unlikely much code in the wild depends on the old behavior. [Proof that it warns today (check console).](https://codesandbox.io/p/sandbox/peaceful-nobel-pdxtfl) --- **The relevant commit is 5696782b428a5ace96e66c1857e13249b6c07958.** It switches `createContext` implementation so that `Context.Provider === Context`. The main assumption that changed is that a Provider's fiber type is now the context itself (rather than an intermediate object). Whereas a Consumer's fiber type is now always an intermediate object (rather than it being sometimes the context itself and sometimes an intermediate object). My methodology was to start with the relevant symbols, work tags, and types, and work my way backwards to all usages. This might break tooling that depends on inspecting React's internal fields. I've added DevTools support in the second commit. This didn't need explicit versioning—the structure tells us enough.
2024-02-13 15:04:49 +00:00
} from './ReactFeatureFlags';
2022-03-07 21:13:22 -08:00
// Keep in sync with react-reconciler/getComponentNameFromFiber
function getWrappedName(
outerType: mixed,
innerType: any,
wrapperName: string,
): string {
const displayName = (outerType: any).displayName;
if (displayName) {
return displayName;
}
const functionName = innerType.displayName || innerType.name || '';
return functionName !== '' ? `${wrapperName}(${functionName})` : wrapperName;
}
// Keep in sync with react-reconciler/getComponentNameFromFiber
function getContextName(type: ReactContext<any>) {
return type.displayName || 'Context';
}
const REACT_CLIENT_REFERENCE = Symbol.for('react.client.reference');
// Note that the reconciler package should generally prefer to use getComponentNameFromFiber() instead.
export default function getComponentNameFromType(type: mixed): string | null {
if (type == null) {
// Host root, text node or just invalid type.
return null;
}
if (typeof type === 'function') {
if ((type: any).$$typeof === REACT_CLIENT_REFERENCE) {
// TODO: Create a convention for naming client references with debug info.
return null;
}
2020-01-24 10:52:38 -08:00
return (type: any).displayName || type.name || null;
}
if (typeof type === 'string') {
return type;
}
switch (type) {
case REACT_FRAGMENT_TYPE:
return 'Fragment';
case REACT_PROFILER_TYPE:
return 'Profiler';
case REACT_STRICT_MODE_TYPE:
return 'StrictMode';
case REACT_SUSPENSE_TYPE:
return 'Suspense';
case REACT_SUSPENSE_LIST_TYPE:
return 'SuspenseList';
case REACT_ACTIVITY_TYPE:
return 'Activity';
Add <ViewTransition> Component (#31975) This will provide the opt-in for using [View Transitions](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API) in React. View Transitions only trigger for async updates like `startTransition`, `useDeferredValue`, Actions or `<Suspense>` revealing from fallback to content. Synchronous updates provide an opt-out but also guarantee that they commit immediately which View Transitions can't. There's no need to opt-in to View Transitions at the "cause" side like event handlers or actions. They don't know what UI will change and whether that has an animated transition described. Conceptually the `<ViewTransition>` component is like a DOM fragment that transitions its children in its own isolate/snapshot. The API works by wrapping a DOM node or inner component: ```js import {ViewTransition} from 'react'; <ViewTransition><Component /></ViewTransition> ``` The default is `name="auto"` which will automatically assign a `view-transition-name` to the inner DOM node. That way you can add a View Transition to a Component without controlling its DOM nodes styling otherwise. A difference between this and the browser's built-in `view-transition-name: auto` is that switching the DOM nodes within the `<ViewTransition>` component preserves the same name so this example cross-fades between the DOM nodes instead of causing an exit and enter: ```js <ViewTransition>{condition ? <ComponentA /> : <ComponentB />}</ViewTransition> ``` This becomes especially useful with `<Suspense>` as this example cross-fades between Skeleton and Content: ```js <ViewTransition> <Suspense fallback={<Skeleton />}> <Content /> </Suspense> </ViewTransition> ``` Where as this example triggers an exit of the Skeleton and an enter of the Content: ```js <Suspense fallback={<ViewTransition><Skeleton /></ViewTransition>}> <ViewTransition><Content /></ViewTransition> </Suspense> ``` Managing instances and keys becomes extra important. You can also specify an explicit `name` property for example for animating the same conceptual item from one page onto another. However, best practices is to property namespace these since they can easily collide. It's also useful to add an `id` to it if available. ```js <ViewTransition name="my-shared-view"> ``` The model in general is the same as plain `view-transition-name` except React manages a set of heuristics for when to apply it. A problem with the naive View Transitions model is that it overly opts in every boundary that *might* transition into transitioning. This is leads to unfortunate effects like things floating around when unrelated updates happen. This leads the whole document to animate which means that nothing is clickable in the meantime. It makes it not useful for smaller and more local transitions. Best practice is to add `view-transition-name` only right before you're about to need to animate the thing. This is tricky to manage globally on complex apps and is not compositional. Instead we let React manage when a `<ViewTransition>` "activates" and add/remove the `view-transition-name`. This is also when React calls `startViewTransition` behind the scenes while it mutates the DOM. I've come up with a number of heuristics that I think will make a lot easier to coordinate this. The principle is that only if something that updates that particular boundary do we activate it. I hope that one day maybe browsers will have something like these built-in and we can remove our implementation. A `<ViewTransition>` only activates if: - If a mounted Component renders a `<ViewTransition>` within it outside the first DOM node, and it is within the viewport, then that ViewTransition activates as an "enter" animation. This avoids inner "enter" animations trigger when the parent mounts. - If an unmounted Component had a `<ViewTransition>` within it outside the first DOM node, and it was within the viewport, then that ViewTransition activates as an "exit" animation. This avoids inner "exit" animations triggering when the parent unmounts. - If an explicitly named `<ViewTransition name="...">` is deep within an unmounted tree and one with the same name appears in a mounted tree at the same time, then both are activated as a pair, but only if they're both in the viewport. This avoids these triggering "enter" or "exit" animations when going between parents that don't have a pair. - If an already mounted `<ViewTransition>` is visible and a DOM mutation, that might affect how it's painted, happens within its children but outside any nested `<ViewTransition>`. This allows it to "cross-fade" between its updates. - If an already mounted `<ViewTransition>` resizes or moves as the result of direct DOM nodes siblings changing or moving around. This allows insertion, deletion and reorders into a list to animate all children. It is only within one DOM node though, to avoid unrelated changes in the parent to trigger this. If an item is outside the viewport before and after, then it's skipped to avoid things flying across the screen. - If a `<ViewTransition>` boundary changes size, due to a DOM mutation within it, then the parent activates (or the root document if there are no more parents). This ensures that the container can cross-fade to avoid abrupt relayout. This can be avoided by using absolutely positioned children. When this can avoid bubbling to the root document, whatever is not animating is still responsive to clicks during the transition. Conceptually each DOM node has its own default that activates the parent `<ViewTransition>` or no transition if the parent is the root. That means that if you add a DOM node like `<div><ViewTransition><Component /></ViewTransition></div>` this won't trigger an "enter" animation since it was the div that was added, not the ViewTransition. Instead, it might cause a cross-fade of the parent ViewTransition or no transition if it had no parent. This ensures that only explicit boundaries perform coarse animations instead of every single node which is really the benefit of the View Transitions model. This ends up working out well for simple cases like switching between two pages immediately while transitioning one floating item that appears on both pages. Because only the floating item transitions by default. Note that it's possible to add manual `view-transition-name` with CSS or `style={{ viewTransitionName: 'auto' }}` that always transitions as long as something else has a `<ViewTransition>` that activates. For example a `<ViewTransition>` can wrap a whole page for a cross-fade but inside of it an explicit name can be added to something to ensure it animates as a move when something relates else changes its layout. Instead of just cross-fading it along with the Page which would be the default. There's more PRs coming with some optimizations, fixes and expanded APIs. This first PR explores the above core heuristic. --------- Co-authored-by: Sebastian "Sebbie" Silbermann <silbermann.sebastian@gmail.com>
2025-01-08 12:11:18 -05:00
case REACT_VIEW_TRANSITION_TYPE:
if (enableViewTransition) {
return 'ViewTransition';
}
// Fall through
case REACT_TRACING_MARKER_TYPE:
2022-03-07 21:13:22 -08:00
if (enableTransitionTracing) {
return 'TracingMarker';
}
}
if (typeof type === 'object') {
if (__DEV__) {
if (typeof (type: any).tag === 'number') {
console.error(
'Received an unexpected object in getComponentNameFromType(). ' +
'This is likely a bug in React. Please file an issue.',
);
}
}
switch (type.$$typeof) {
case REACT_PORTAL_TYPE:
return 'Portal';
case REACT_CONTEXT_TYPE:
const context: ReactContext<any> = (type: any);
return getContextName(context);
Switch <Context> to mean <Context.Provider> (#28226) Previously, `<Context>` was equivalent to `<Context.Consumer>`. However, since the introduction of Hooks, the `<Context.Consumer>` API is rarely used. The goal here is to make the common case cleaner: ```js const ThemeContext = createContext('light') function App() { return ( <ThemeContext value="dark"> ... </ThemeContext> ) } function Button() { const theme = use(ThemeContext) // ... } ``` This is technically a breaking change, but we've been warning about rendering `<Context>` directly for several years by now, so it's unlikely much code in the wild depends on the old behavior. [Proof that it warns today (check console).](https://codesandbox.io/p/sandbox/peaceful-nobel-pdxtfl) --- **The relevant commit is 5696782b428a5ace96e66c1857e13249b6c07958.** It switches `createContext` implementation so that `Context.Provider === Context`. The main assumption that changed is that a Provider's fiber type is now the context itself (rather than an intermediate object). Whereas a Consumer's fiber type is now always an intermediate object (rather than it being sometimes the context itself and sometimes an intermediate object). My methodology was to start with the relevant symbols, work tags, and types, and work my way backwards to all usages. This might break tooling that depends on inspecting React's internal fields. I've added DevTools support in the second commit. This didn't need explicit versioning—the structure tells us enough.
2024-02-13 15:04:49 +00:00
case REACT_CONSUMER_TYPE:
const consumer: ReactConsumerType<any> = (type: any);
return getContextName(consumer._context) + '.Consumer';
case REACT_FORWARD_REF_TYPE:
return getWrappedName(type, type.render, 'ForwardRef');
2018-10-20 12:46:23 -04:00
case REACT_MEMO_TYPE:
const outerName = (type: any).displayName || null;
if (outerName !== null) {
return outerName;
}
return getComponentNameFromType(type.type) || 'Memo';
case REACT_LAZY_TYPE: {
const lazyComponent: LazyComponent<any, any> = (type: any);
const payload = lazyComponent._payload;
const init = lazyComponent._init;
try {
return getComponentNameFromType(init(payload));
} catch (x) {
return null;
}
}
Accept promise as element type (#13397) * Accept promise as element type On the initial render, the element will suspend as if a promise were thrown from inside the body of the unresolved component. Siblings should continue rendering and if the parent is a Placeholder, the promise should be captured by that Placeholder. When the promise resolves, rendering resumes. If the resolved value has a `default` property, it is assumed to be the default export of an ES module, and we use that as the component type. If it does not have a `default` property, we use the resolved value itself. The resolved value is stored as an expando on the promise/thenable. * Use special types of work for lazy components Because reconciliation is a hot path, this adds ClassComponentLazy, FunctionalComponentLazy, and ForwardRefLazy as special types of work. The other types are not supported, but wouldn't be placed into a separate module regardless. * Resolve defaultProps for lazy types * Remove some calls to isContextProvider isContextProvider checks the fiber tag, but it's typically called after we've already refined the type of work. We should get rid of it. I removed some of them in the previous commit, and deleted a few more in this one. I left a few behind because the remaining ones would require additional refactoring that feels outside the scope of this PR. * Remove getLazyComponentTypeIfResolved * Return baseProps instead of null The caller compares the result to baseProps to see if anything changed. * Avoid redundant checks by inlining getFiberTagFromObjectType * Move tag resolution to ReactFiber module * Pass next props to update* functions We should do this with all types of work in the future. * Refine component type before pushing/popping context Removes unnecessary checks. * Replace all occurrences of _reactResult with helper * Move shared thenable logic to `shared` package * Check type of wrapper object before resolving to `default` export * Return resolved tag instead of reassigning
2018-08-16 09:21:59 -07:00
}
}
return null;
}