Skip to content

Commit acca79d

Browse files
committed
Convert string ref props to callback props (#28398)
When enableRefAsProp is on, we should always use the props as the source of truth for refs. Not a field on the fiber. In the case of string refs, this presents a problem, because string refs are not passed around internally as strings; they are converted to callback refs. The ref used by the reconciler is not the same as the one the user provided. But since this is a deprecated feature anyway, what we can do is clone the props object and replace it with the internal callback ref. Then we can continue to use the props object as the source of truth. This means the internal callback ref will leak into userspace. The receiving component will receive a callback ref even though the parent passed a string. Which is weird, but again, this is a deprecated feature, and we're only leaving it around behind a flag so that Meta can keep using string refs temporarily while they finish migrating their codebase. DiffTrain build for [dc30644](dc30644)
1 parent d319f43 commit acca79d

19 files changed

+1313
-1265
lines changed

compiled/facebook-www/REVISION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
353ecd05160a318a3f75260ee7906fd12e05cb9d
1+
dc30644ca77e52a2760e81fbdbcfbd2f2fd4979c

compiled/facebook-www/React-prod.classic.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -628,4 +628,4 @@ exports.useSyncExternalStore = function (
628628
exports.useTransition = function () {
629629
return ReactCurrentDispatcher.current.useTransition();
630630
};
631-
exports.version = "18.3.0-www-classic-a3d0776b";
631+
exports.version = "18.3.0-www-classic-9dbcb358";

compiled/facebook-www/React-profiling.classic.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@ exports.useSyncExternalStore = function (
632632
exports.useTransition = function () {
633633
return ReactCurrentDispatcher.current.useTransition();
634634
};
635-
exports.version = "18.3.0-www-classic-3d32b702";
635+
exports.version = "18.3.0-www-classic-1cd2cf0b";
636636
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
637637
"function" ===
638638
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/ReactART-dev.classic.js

+119-108
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ if (__DEV__) {
6666
return self;
6767
}
6868

69-
var ReactVersion = "18.3.0-www-classic-cd924c55";
69+
var ReactVersion = "18.3.0-www-classic-cbef9601";
7070

7171
var LegacyRoot = 0;
7272
var ConcurrentRoot = 1;
@@ -6352,127 +6352,138 @@ if (__DEV__) {
63526352
return trackUsedThenable(thenableState$1, thenable, index);
63536353
}
63546354

6355-
function coerceRef(returnFiber, current, element) {
6356-
var mixedRef;
6357-
6358-
{
6359-
// Old behavior.
6360-
mixedRef = element.ref;
6361-
}
6362-
6363-
if (
6364-
mixedRef !== null &&
6365-
typeof mixedRef !== "function" &&
6366-
typeof mixedRef !== "object"
6367-
) {
6368-
{
6369-
if (
6370-
// Will already throw with "Function components cannot have string refs"
6371-
!(element._owner && element._owner.tag !== ClassComponent) && // Will already warn with "Function components cannot be given refs"
6372-
!(
6373-
typeof element.type === "function" && !isReactClass(element.type)
6374-
) && // Will already throw with "Element ref was specified as a string (someStringRef) but no owner was set"
6375-
element._owner
6376-
) {
6377-
var componentName =
6378-
getComponentNameFromFiber(returnFiber) || "Component";
6379-
6380-
if (!didWarnAboutStringRefs[componentName]) {
6381-
error(
6382-
'Component "%s" contains the string ref "%s". Support for string refs ' +
6383-
"will be removed in a future major release. We recommend using " +
6384-
"useRef() or createRef() instead. " +
6385-
"Learn more about using refs safely here: " +
6386-
"https://reactjs.org/link/strict-mode-string-ref",
6387-
componentName,
6388-
mixedRef
6389-
);
6355+
function convertStringRefToCallbackRef(
6356+
returnFiber,
6357+
current,
6358+
element,
6359+
mixedRef
6360+
) {
6361+
var owner = element._owner;
63906362

6391-
didWarnAboutStringRefs[componentName] = true;
6392-
}
6393-
}
6363+
if (!owner) {
6364+
if (typeof mixedRef !== "string") {
6365+
throw new Error(
6366+
"Expected ref to be a function, a string, an object returned by React.createRef(), or null."
6367+
);
63946368
}
63956369

6396-
if (element._owner) {
6397-
var owner = element._owner;
6398-
var inst;
6370+
throw new Error(
6371+
"Element ref was specified as a string (" +
6372+
mixedRef +
6373+
") but no owner was set. This could happen for one of" +
6374+
" the following reasons:\n" +
6375+
"1. You may be adding a ref to a function component\n" +
6376+
"2. You may be adding a ref to a component that was not created inside a component's render method\n" +
6377+
"3. You have multiple copies of React loaded\n" +
6378+
"See https://reactjs.org/link/refs-must-have-owner for more information."
6379+
);
6380+
}
63996381

6400-
if (owner) {
6401-
var ownerFiber = owner;
6382+
if (owner.tag !== ClassComponent) {
6383+
throw new Error(
6384+
"Function components cannot have string refs. " +
6385+
"We recommend using useRef() instead. " +
6386+
"Learn more about using refs safely here: " +
6387+
"https://reactjs.org/link/strict-mode-string-ref"
6388+
);
6389+
} // At this point, we know the ref isn't an object or function but it could
6390+
// be a number. Coerce it to a string.
64026391

6403-
if (ownerFiber.tag !== ClassComponent) {
6404-
throw new Error(
6405-
"Function components cannot have string refs. " +
6406-
"We recommend using useRef() instead. " +
6407-
"Learn more about using refs safely here: " +
6408-
"https://reactjs.org/link/strict-mode-string-ref"
6409-
);
6410-
}
6392+
{
6393+
checkPropStringCoercion(mixedRef, "ref");
6394+
}
64116395

6412-
inst = ownerFiber.stateNode;
6413-
}
6396+
var stringRef = "" + mixedRef;
64146397

6415-
if (!inst) {
6416-
throw new Error(
6417-
"Missing owner for string ref " +
6418-
mixedRef +
6419-
". This error is likely caused by a " +
6420-
"bug in React. Please file an issue."
6421-
);
6422-
} // Assigning this to a const so Flow knows it won't change in the closure
6398+
{
6399+
if (
6400+
// Will already warn with "Function components cannot be given refs"
6401+
!(typeof element.type === "function" && !isReactClass(element.type))
6402+
) {
6403+
var componentName =
6404+
getComponentNameFromFiber(returnFiber) || "Component";
64236405

6424-
var resolvedInst = inst;
6406+
if (!didWarnAboutStringRefs[componentName]) {
6407+
error(
6408+
'Component "%s" contains the string ref "%s". Support for string refs ' +
6409+
"will be removed in a future major release. We recommend using " +
6410+
"useRef() or createRef() instead. " +
6411+
"Learn more about using refs safely here: " +
6412+
"https://reactjs.org/link/strict-mode-string-ref",
6413+
componentName,
6414+
stringRef
6415+
);
64256416

6426-
{
6427-
checkPropStringCoercion(mixedRef, "ref");
6417+
didWarnAboutStringRefs[componentName] = true;
64286418
}
6419+
}
6420+
}
64296421

6430-
var stringRef = "" + mixedRef; // Check if previous string ref matches new string ref
6422+
var inst = owner.stateNode;
64316423

6432-
if (
6433-
current !== null &&
6434-
current.ref !== null &&
6435-
typeof current.ref === "function" &&
6436-
current.ref._stringRef === stringRef
6437-
) {
6438-
return current.ref;
6439-
}
6424+
if (!inst) {
6425+
throw new Error(
6426+
"Missing owner for string ref " +
6427+
stringRef +
6428+
". This error is likely caused by a " +
6429+
"bug in React. Please file an issue."
6430+
);
6431+
} // Check if previous string ref matches new string ref
64406432

6441-
var ref = function (value) {
6442-
var refs = resolvedInst.refs;
6433+
if (
6434+
current !== null &&
6435+
current.ref !== null &&
6436+
typeof current.ref === "function" &&
6437+
current.ref._stringRef === stringRef
6438+
) {
6439+
// Reuse the existing string ref
6440+
var currentRef = current.ref;
6441+
return currentRef;
6442+
} // Create a new string ref
64436443

6444-
if (value === null) {
6445-
delete refs[stringRef];
6446-
} else {
6447-
refs[stringRef] = value;
6448-
}
6449-
};
6444+
var ref = function (value) {
6445+
var refs = inst.refs;
64506446

6451-
ref._stringRef = stringRef;
6452-
return ref;
6447+
if (value === null) {
6448+
delete refs[stringRef];
64536449
} else {
6454-
if (typeof mixedRef !== "string") {
6455-
throw new Error(
6456-
"Expected ref to be a function, a string, an object returned by React.createRef(), or null."
6457-
);
6458-
}
6459-
6460-
if (!element._owner) {
6461-
throw new Error(
6462-
"Element ref was specified as a string (" +
6463-
mixedRef +
6464-
") but no owner was set. This could happen for one of" +
6465-
" the following reasons:\n" +
6466-
"1. You may be adding a ref to a function component\n" +
6467-
"2. You may be adding a ref to a component that was not created inside a component's render method\n" +
6468-
"3. You have multiple copies of React loaded\n" +
6469-
"See https://reactjs.org/link/refs-must-have-owner for more information."
6470-
);
6471-
}
6450+
refs[stringRef] = value;
64726451
}
6452+
};
6453+
6454+
ref._stringRef = stringRef;
6455+
return ref;
6456+
}
6457+
6458+
function coerceRef(returnFiber, current, workInProgress, element) {
6459+
var mixedRef;
6460+
6461+
{
6462+
// Old behavior.
6463+
mixedRef = element.ref;
64736464
}
64746465

6475-
return mixedRef;
6466+
var coercedRef;
6467+
6468+
if (
6469+
mixedRef !== null &&
6470+
typeof mixedRef !== "function" &&
6471+
typeof mixedRef !== "object"
6472+
) {
6473+
// Assume this is a string ref. If it's not, then this will throw an error
6474+
// to the user.
6475+
coercedRef = convertStringRefToCallbackRef(
6476+
returnFiber,
6477+
current,
6478+
element,
6479+
mixedRef
6480+
);
6481+
} else {
6482+
coercedRef = mixedRef;
6483+
} // TODO: If enableRefAsProp is on, we shouldn't use the `ref` field. We
6484+
// should always read the ref from the prop.
6485+
6486+
workInProgress.ref = coercedRef;
64766487
}
64776488

64786489
function throwOnInvalidObjectType(returnFiber, newChild) {
@@ -6728,7 +6739,7 @@ if (__DEV__) {
67286739
) {
67296740
// Move based on index
67306741
var existing = useFiber(current, element.props);
6731-
existing.ref = coerceRef(returnFiber, current, element);
6742+
coerceRef(returnFiber, current, existing, element);
67326743
existing.return = returnFiber;
67336744

67346745
{
@@ -6741,7 +6752,7 @@ if (__DEV__) {
67416752
} // Insert
67426753

67436754
var created = createFiberFromElement(element, returnFiber.mode, lanes);
6744-
created.ref = coerceRef(returnFiber, current, element);
6755+
coerceRef(returnFiber, current, created, element);
67456756
created.return = returnFiber;
67466757

67476758
{
@@ -6847,7 +6858,7 @@ if (__DEV__) {
68476858
lanes
68486859
);
68496860

6850-
_created.ref = coerceRef(returnFiber, null, newChild);
6861+
coerceRef(returnFiber, null, _created, newChild);
68516862
_created.return = returnFiber;
68526863

68536864
{
@@ -7710,7 +7721,7 @@ if (__DEV__) {
77107721

77117722
var _existing = useFiber(child, element.props);
77127723

7713-
_existing.ref = coerceRef(returnFiber, child, element);
7724+
coerceRef(returnFiber, child, _existing, element);
77147725
_existing.return = returnFiber;
77157726

77167727
{
@@ -7752,7 +7763,7 @@ if (__DEV__) {
77527763
lanes
77537764
);
77547765

7755-
_created4.ref = coerceRef(returnFiber, currentFirstChild, element);
7766+
coerceRef(returnFiber, currentFirstChild, _created4, element);
77567767
_created4.return = returnFiber;
77577768

77587769
{

0 commit comments

Comments
 (0)