Skip to content

Commit

Permalink
Pass ref as normal prop (#28348)
Browse files Browse the repository at this point in the history
Depends on:

- #28317
- #28320

---

Changes the behavior of the JSX runtime to pass through `ref` as a
normal prop, rather than plucking it from the props object and storing
on the element.

This is a breaking change since it changes the type of the receiving
component. However, most code is unaffected since it's unlikely that a
component would have attempted to access a `ref` prop, since it was not
possible to get a reference to one.

`forwardRef` _will_ still pluck `ref` from the props object, though,
since it's extremely common for users to spread the props object onto
the inner component and pass `ref` as a differently named prop. This is
for maximum compatibility with existing code — the real impact of this
change is that `forwardRef` is no longer required.

Currently, refs are resolved during child reconciliation and stored on
the fiber. As a result of this change, we can move ref resolution to
happen only much later, and only for components that actually use them.
Then we can remove the `ref` field from the Fiber type. I have not yet
done that in this step, though.

DiffTrain build for commit fa2f82a.
  • Loading branch information
acdlite committed Feb 20, 2024
1 parent 5a8f5c8 commit c25dcdb
Show file tree
Hide file tree
Showing 11 changed files with 232 additions and 138 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<bdf0df21b100941fad1618d2f0f4ea03>>
* @generated SignedSource<<1d3d484449a45d9d48c574d191f78a9b>>
*/

"use strict";
Expand Down Expand Up @@ -5040,7 +5040,12 @@ if (__DEV__) {
}

function coerceRef(returnFiber, current, element) {
var mixedRef = element.ref;
var mixedRef;

{
// Old behavior.
mixedRef = element.ref;
}

if (
mixedRef !== null &&
Expand Down Expand Up @@ -12692,7 +12697,12 @@ if (__DEV__) {
// hasn't yet mounted. This happens after the first render suspends.
// We'll need to figure out if this is fine or can cause issues.
var render = Component.render;
var ref = workInProgress.ref; // The rest is a fork of updateFunctionComponent
var ref = workInProgress.ref;
var propsWithoutRef;

{
propsWithoutRef = nextProps;
} // The rest is a fork of updateFunctionComponent

var nextChildren;
prepareToReadContext(workInProgress, renderLanes);
Expand All @@ -12704,7 +12714,7 @@ if (__DEV__) {
current,
workInProgress,
render,
nextProps,
propsWithoutRef,
ref,
renderLanes
);
Expand Down Expand Up @@ -25686,7 +25696,7 @@ if (__DEV__) {
return root;
}

var ReactVersion = "18.3.0-canary-7b196be09-20240220";
var ReactVersion = "18.3.0-canary-fa2f82add-20240220";

// Might add PROFILE later.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9176,7 +9176,7 @@ var devToolsConfig$jscomp$inline_1014 = {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 0,
version: "18.3.0-canary-7b196be09-20240220",
version: "18.3.0-canary-fa2f82add-20240220",
rendererPackageName: "react-test-renderer"
};
var internals$jscomp$inline_1195 = {
Expand Down Expand Up @@ -9207,7 +9207,7 @@ var internals$jscomp$inline_1195 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "18.3.0-canary-7b196be09-20240220"
reconcilerVersion: "18.3.0-canary-fa2f82add-20240220"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1196 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9604,7 +9604,7 @@ var devToolsConfig$jscomp$inline_1056 = {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 0,
version: "18.3.0-canary-7b196be09-20240220",
version: "18.3.0-canary-fa2f82add-20240220",
rendererPackageName: "react-test-renderer"
};
var internals$jscomp$inline_1236 = {
Expand Down Expand Up @@ -9635,7 +9635,7 @@ var internals$jscomp$inline_1236 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "18.3.0-canary-7b196be09-20240220"
reconcilerVersion: "18.3.0-canary-fa2f82add-20240220"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1237 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<9d1803d74418a7ac44bc89ae8cd0f170>>
* @generated SignedSource<<20a889fc4c8eaee85875c7091bc5ea65>>
*/

"use strict";
Expand Down Expand Up @@ -643,25 +643,27 @@ if (__DEV__) {

function defineRefPropWarningGetter(props, displayName) {
{
var warnAboutAccessingRef = function () {
if (!specialPropRefWarningShown) {
specialPropRefWarningShown = true;
{
var warnAboutAccessingRef = function () {
if (!specialPropRefWarningShown) {
specialPropRefWarningShown = true;

error(
"%s: `ref` is not a prop. Trying to access it will result " +
"in `undefined` being returned. If you need to access the same " +
"value within the child component, you should pass it as a different " +
"prop. (https://reactjs.org/link/special-props)",
displayName
);
}
};
error(
"%s: `ref` is not a prop. Trying to access it will result " +
"in `undefined` being returned. If you need to access the same " +
"value within the child component, you should pass it as a different " +
"prop. (https://reactjs.org/link/special-props)",
displayName
);
}
};

warnAboutAccessingRef.isReactWarning = true;
Object.defineProperty(props, "ref", {
get: warnAboutAccessingRef,
configurable: true
});
warnAboutAccessingRef.isReactWarning = true;
Object.defineProperty(props, "ref", {
get: warnAboutAccessingRef,
configurable: true
});
}
}
}
/**
Expand All @@ -685,18 +687,30 @@ if (__DEV__) {
* @internal
*/

function ReactElement(type, key, ref, self, source, owner, props) {
var element = {
// This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
type: type,
key: key,
ref: ref,
props: props,
// Record the component responsible for creating this element.
_owner: owner
};
function ReactElement(type, key, _ref, self, source, owner, props) {
var ref;

{
ref = _ref;
}

var element;

{
// In prod, `ref` is a regular property. It will be removed in a
// future release.
element = {
// This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
type: type,
key: key,
ref: ref,
props: props,
// Record the component responsible for creating this element.
_owner: owner
};
}

{
// The validation flag is currently mutative. We put it on
Expand Down Expand Up @@ -882,14 +896,17 @@ if (__DEV__) {
}

if (hasValidRef(config)) {
ref = config.ref;
{
ref = config.ref;
}

warnIfStringRefCannotBeAutoConverted(config, self);
} // Remaining properties are added to a new props object

for (propName in config) {
if (
hasOwnProperty.call(config, propName) && // Skip over reserved prop names
propName !== "key" && // TODO: `ref` will no longer be reserved in the next major
propName !== "key" &&
propName !== "ref"
) {
props[propName] = config[propName];
Expand Down Expand Up @@ -1130,6 +1147,7 @@ if (__DEV__) {
*/

function validateFragmentProps(fragment) {
// TODO: Move this to render phase instead of at element creation.
{
var keys = Object.keys(fragment.props);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<8af9696c59d9d4cfa4cc835b5a54e94e>>
* @generated SignedSource<<74f1366b0384318710be72f745716987>>
*/

"use strict";
Expand Down Expand Up @@ -643,25 +643,27 @@ if (__DEV__) {

function defineRefPropWarningGetter(props, displayName) {
{
var warnAboutAccessingRef = function () {
if (!specialPropRefWarningShown) {
specialPropRefWarningShown = true;
{
var warnAboutAccessingRef = function () {
if (!specialPropRefWarningShown) {
specialPropRefWarningShown = true;

error(
"%s: `ref` is not a prop. Trying to access it will result " +
"in `undefined` being returned. If you need to access the same " +
"value within the child component, you should pass it as a different " +
"prop. (https://reactjs.org/link/special-props)",
displayName
);
}
};
error(
"%s: `ref` is not a prop. Trying to access it will result " +
"in `undefined` being returned. If you need to access the same " +
"value within the child component, you should pass it as a different " +
"prop. (https://reactjs.org/link/special-props)",
displayName
);
}
};

warnAboutAccessingRef.isReactWarning = true;
Object.defineProperty(props, "ref", {
get: warnAboutAccessingRef,
configurable: true
});
warnAboutAccessingRef.isReactWarning = true;
Object.defineProperty(props, "ref", {
get: warnAboutAccessingRef,
configurable: true
});
}
}
}
/**
Expand All @@ -685,18 +687,30 @@ if (__DEV__) {
* @internal
*/

function ReactElement(type, key, ref, self, source, owner, props) {
var element = {
// This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
type: type,
key: key,
ref: ref,
props: props,
// Record the component responsible for creating this element.
_owner: owner
};
function ReactElement(type, key, _ref, self, source, owner, props) {
var ref;

{
ref = _ref;
}

var element;

{
// In prod, `ref` is a regular property. It will be removed in a
// future release.
element = {
// This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
type: type,
key: key,
ref: ref,
props: props,
// Record the component responsible for creating this element.
_owner: owner
};
}

{
// The validation flag is currently mutative. We put it on
Expand Down Expand Up @@ -918,14 +932,17 @@ if (__DEV__) {
}

if (hasValidRef(config)) {
ref = config.ref;
{
ref = config.ref;
}

warnIfStringRefCannotBeAutoConverted(config, self);
} // Remaining properties are added to a new props object

for (propName in config) {
if (
hasOwnProperty.call(config, propName) && // Skip over reserved prop names
propName !== "key" && // TODO: `ref` will no longer be reserved in the next major
propName !== "key" &&
propName !== "ref"
) {
props[propName] = config[propName];
Expand Down Expand Up @@ -1166,6 +1183,7 @@ if (__DEV__) {
*/

function validateFragmentProps(fragment) {
// TODO: Move this to render phase instead of at element creation.
{
var keys = Object.keys(fragment.props);

Expand Down
Loading

0 comments on commit c25dcdb

Please sign in to comment.