Skip to content

Commit

Permalink
Move ref type check to receiver (#28464)
Browse files Browse the repository at this point in the history
The runtime contains a type check to determine if a user-provided ref is
a valid type — a function or object (or a string, when
`disableStringRefs` is off). This currently happens during child
reconciliation. This changes it to happen only when the ref is passed to
the component that the ref is being attached to.

This is a continuation of the "ref as prop" change — until you actually
pass a ref to a HostComponent, class, etc, ref is a normal prop that has
no special behavior.

DiffTrain build for commit 2f8f776.
  • Loading branch information
acdlite committed Mar 1, 2024
1 parent dbab28d commit fc46af0
Show file tree
Hide file tree
Showing 13 changed files with 220 additions and 214 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<c7f09b0ccfd717d023bd590c0cbfdace>>
* @generated SignedSource<<514c453e36455da8cb22e0c8c9169c24>>
*/

"use strict";
Expand Down Expand Up @@ -5067,18 +5067,17 @@ if (__DEV__) {
element,
mixedRef
) {
{
checkPropStringCoercion(mixedRef, "ref");
}

var stringRef = "" + mixedRef;
var owner = element._owner;

if (!owner) {
if (typeof mixedRef !== "string") {
throw new Error(
"Expected ref to be a function, a string, an object returned by React.createRef(), or null."
);
}

throw new Error(
"Element ref was specified as a string (" +
mixedRef +
stringRef +
") but no owner was set. This could happen for one of" +
" the following reasons:\n" +
"1. You may be adding a ref to a function component\n" +
Expand All @@ -5095,15 +5094,8 @@ if (__DEV__) {
"Learn more about using refs safely here: " +
"https://reactjs.org/link/strict-mode-string-ref"
);
} // At this point, we know the ref isn't an object or function but it could
// be a number. Coerce it to a string.

{
checkPropStringCoercion(mixedRef, "ref");
}

var stringRef = "" + mixedRef;

{
if (
// Will already warn with "Function components cannot be given refs"
Expand Down Expand Up @@ -5175,12 +5167,10 @@ if (__DEV__) {
var coercedRef;

if (
mixedRef !== null &&
typeof mixedRef !== "function" &&
typeof mixedRef !== "object"
typeof mixedRef === "string" ||
typeof mixedRef === "number" ||
typeof mixedRef === "boolean"
) {
// Assume this is a string ref. If it's not, then this will throw an error
// to the user.
coercedRef = convertStringRefToCallbackRef(
returnFiber,
current,
Expand Down Expand Up @@ -13232,17 +13222,25 @@ if (__DEV__) {
}

function markRef(current, workInProgress) {
// TODO: This is also where we should check the type of the ref and error if
// an invalid one is passed, instead of during child reconcilation.
// TODO: Check props.ref instead of fiber.ref when enableRefAsProp is on.
var ref = workInProgress.ref;

if (
(current === null && ref !== null) ||
(current !== null && current.ref !== ref)
) {
// Schedule a Ref effect
workInProgress.flags |= Ref;
workInProgress.flags |= RefStatic;
if (ref === null) {
if (current !== null && current.ref !== null) {
// Schedule a Ref effect
workInProgress.flags |= Ref | RefStatic;
}
} else {
if (typeof ref !== "function" && typeof ref !== "object") {
throw new Error(
"Expected ref to be a function, an object returned by React.createRef(), or undefined/null."
);
}

if (current === null || current.ref !== ref) {
// Schedule a Ref effect
workInProgress.flags |= Ref | RefStatic;
}
}
}

Expand Down Expand Up @@ -25735,7 +25733,7 @@ if (__DEV__) {
return root;
}

var ReactVersion = "18.3.0-canary-bb4b147da-20240229";
var ReactVersion = "18.3.0-canary-2f8f77602-20240229";

// Might add PROFILE later.

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

"use strict";
Expand Down Expand Up @@ -1464,24 +1464,19 @@ function convertStringRefToCallbackRef(
var refs = inst.refs;
null === value ? delete refs[stringRef] : (refs[stringRef] = value);
}
var stringRef = "" + mixedRef;
returnFiber = element._owner;
if (!returnFiber) {
if ("string" !== typeof mixedRef)
throw Error(
"Expected ref to be a function, a string, an object returned by React.createRef(), or null."
);
if (!returnFiber)
throw Error(
"Element ref was specified as a string (" +
mixedRef +
stringRef +
") but no owner was set. This could happen for one of the following reasons:\n1. You may be adding a ref to a function component\n2. You may be adding a ref to a component that was not created inside a component's render method\n3. You have multiple copies of React loaded\nSee https://reactjs.org/link/refs-must-have-owner for more information."
);
}
if (1 !== returnFiber.tag)
throw Error(
"Function components cannot have string refs. We recommend using useRef() instead. Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref"
);
var stringRef = "" + mixedRef,
inst = returnFiber.stateNode;
var inst = returnFiber.stateNode;
if (!inst)
throw Error(
"Missing owner for string ref " +
Expand All @@ -1501,9 +1496,9 @@ function convertStringRefToCallbackRef(
function coerceRef(returnFiber, current, workInProgress, element) {
var mixedRef = element.ref;
returnFiber =
null !== mixedRef &&
"function" !== typeof mixedRef &&
"object" !== typeof mixedRef
"string" === typeof mixedRef ||
"number" === typeof mixedRef ||
"boolean" === typeof mixedRef
? convertStringRefToCallbackRef(returnFiber, current, element, mixedRef)
: mixedRef;
workInProgress.ref = returnFiber;
Expand Down Expand Up @@ -3986,11 +3981,18 @@ function deferHiddenOffscreenComponent(current, workInProgress, nextBaseLanes) {
}
function markRef(current, workInProgress) {
var ref = workInProgress.ref;
if (
(null === current && null !== ref) ||
(null !== current && current.ref !== ref)
)
(workInProgress.flags |= 512), (workInProgress.flags |= 2097152);
if (null === ref)
null !== current &&
null !== current.ref &&
(workInProgress.flags |= 2097664);
else {
if ("function" !== typeof ref && "object" !== typeof ref)
throw Error(
"Expected ref to be a function, an object returned by React.createRef(), or undefined/null."
);
if (null === current || current.ref !== ref)
workInProgress.flags |= 2097664;
}
}
function updateFunctionComponent(
current,
Expand Down Expand Up @@ -9171,7 +9173,7 @@ var devToolsConfig$jscomp$inline_1014 = {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 0,
version: "18.3.0-canary-bb4b147da-20240229",
version: "18.3.0-canary-2f8f77602-20240229",
rendererPackageName: "react-test-renderer"
};
var internals$jscomp$inline_1195 = {
Expand Down Expand Up @@ -9202,7 +9204,7 @@ var internals$jscomp$inline_1195 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "18.3.0-canary-bb4b147da-20240229"
reconcilerVersion: "18.3.0-canary-2f8f77602-20240229"
};
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 @@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<86e7f77200b19412957b194553a75177>>
* @generated SignedSource<<749ebcbb693ef8635dc1c79f3b3a628d>>
*/

"use strict";
Expand Down Expand Up @@ -1484,24 +1484,19 @@ function convertStringRefToCallbackRef(
var refs = inst.refs;
null === value ? delete refs[stringRef] : (refs[stringRef] = value);
}
var stringRef = "" + mixedRef;
returnFiber = element._owner;
if (!returnFiber) {
if ("string" !== typeof mixedRef)
throw Error(
"Expected ref to be a function, a string, an object returned by React.createRef(), or null."
);
if (!returnFiber)
throw Error(
"Element ref was specified as a string (" +
mixedRef +
stringRef +
") but no owner was set. This could happen for one of the following reasons:\n1. You may be adding a ref to a function component\n2. You may be adding a ref to a component that was not created inside a component's render method\n3. You have multiple copies of React loaded\nSee https://reactjs.org/link/refs-must-have-owner for more information."
);
}
if (1 !== returnFiber.tag)
throw Error(
"Function components cannot have string refs. We recommend using useRef() instead. Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref"
);
var stringRef = "" + mixedRef,
inst = returnFiber.stateNode;
var inst = returnFiber.stateNode;
if (!inst)
throw Error(
"Missing owner for string ref " +
Expand All @@ -1521,9 +1516,9 @@ function convertStringRefToCallbackRef(
function coerceRef(returnFiber, current, workInProgress, element) {
var mixedRef = element.ref;
returnFiber =
null !== mixedRef &&
"function" !== typeof mixedRef &&
"object" !== typeof mixedRef
"string" === typeof mixedRef ||
"number" === typeof mixedRef ||
"boolean" === typeof mixedRef
? convertStringRefToCallbackRef(returnFiber, current, element, mixedRef)
: mixedRef;
workInProgress.ref = returnFiber;
Expand Down Expand Up @@ -4068,11 +4063,18 @@ function deferHiddenOffscreenComponent(current, workInProgress, nextBaseLanes) {
}
function markRef(current, workInProgress) {
var ref = workInProgress.ref;
if (
(null === current && null !== ref) ||
(null !== current && current.ref !== ref)
)
(workInProgress.flags |= 512), (workInProgress.flags |= 2097152);
if (null === ref)
null !== current &&
null !== current.ref &&
(workInProgress.flags |= 2097664);
else {
if ("function" !== typeof ref && "object" !== typeof ref)
throw Error(
"Expected ref to be a function, an object returned by React.createRef(), or undefined/null."
);
if (null === current || current.ref !== ref)
workInProgress.flags |= 2097664;
}
}
function updateFunctionComponent(
current,
Expand Down Expand Up @@ -9599,7 +9601,7 @@ var devToolsConfig$jscomp$inline_1056 = {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 0,
version: "18.3.0-canary-bb4b147da-20240229",
version: "18.3.0-canary-2f8f77602-20240229",
rendererPackageName: "react-test-renderer"
};
var internals$jscomp$inline_1236 = {
Expand Down Expand Up @@ -9630,7 +9632,7 @@ var internals$jscomp$inline_1236 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "18.3.0-canary-bb4b147da-20240229"
reconcilerVersion: "18.3.0-canary-2f8f77602-20240229"
};
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 @@ -26,7 +26,7 @@ if (__DEV__) {
}
var dynamicFlags = require("ReactNativeInternalFeatureFlags");

var ReactVersion = "18.3.0-canary-bb4b147da-20240229";
var ReactVersion = "18.3.0-canary-2f8f77602-20240229";

// ATTENTION
// When adding new symbols to this file,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -598,4 +598,4 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactCurrentDispatcher.current.useTransition();
};
exports.version = "18.3.0-canary-bb4b147da-20240229";
exports.version = "18.3.0-canary-2f8f77602-20240229";
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactCurrentDispatcher.current.useTransition();
};
exports.version = "18.3.0-canary-bb4b147da-20240229";
exports.version = "18.3.0-canary-2f8f77602-20240229";
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
"function" ===
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
bb4b147da9a892529995f55f15f19f46a00cf4f6
2f8f7760223241665f472a2a9be16650473bce39
Loading

0 comments on commit fc46af0

Please sign in to comment.