Skip to content

Commit 1180f1b

Browse files
committed
[DevTools] Make Element Inspection Feel Snappy (facebook#30555)
There's two problems. The biggest one is that it turns out that Chrome is throttling looping timers that we're using both while polling and for batching bridge traffic. This means that bridge traffic a lot of the time just slows down to 1 second at a time. No wonder it feels sluggish. The only solution is to not use timers for this. Even when it doesn't like in Firefox the batching into 100ms still feels too sluggish. The fix I use is to batch using a microtask instead so we can still batch multiple commands sent in a single event but we never artificially slow down an interaction. I don't think we've reevaluated this for a long time since this was in the initial commit of DevTools to this repo. If it causes other issues we can follow up on those. We really shouldn't use timers for debouncing and such. In fact, React itself recommends against it because we have a better technique with scheduling in Concurrent Mode. The correct way to implement this in the bridge is using a form of back-pressure where we don't keep sending messages until we get a message back and only send the last one that matters. E.g. when moving the cursor over a the elements tab we shouldn't let the backend one-by-one move the DOM node to each one we have ever passed. We should just move to the last one we're currently hovering over. But this can't be done at the bridge layer since it doesn't know if it's a last-one-wins or imperative operation where each one needs to be sent. It needs to be done higher. I'm not currently seeing any perf problems with this new approach but I'm curious on React Native or some thing. RN might need the back-pressure approach. That can be a follow up if we ever find a test case. Finally, the other problem is that we use a Suspense boundary around the Element Inspection. Suspense boundaries are for things that are expected to take a long time to load. This shows a loading state immediately. To avoid flashing when it ends up being fast, React throttles the reveal to 200ms. This means that we take a minimum of 200ms to show the props. The way to show fast async data in React is using a Transition (either using startTransition or useDeferredValue). This lets the old value remaining in place while we're loading the next one. We already implement this using `inspectedElementID` which is the async one. It would be more idiomatic to implement this with useDeferredValue rather than the reducer we have now but same principle. We were just using the wrong ID in a few places so when it synchronously updated they suspended. So I just made them use the inspectedElementID instead. Then I can simply remove the Suspense boundary. Now the selection updates in the tree view synchronously and the sidebar lags a frame or two but it feels instant. It doesn't flash to white between which is key. DiffTrain build for [4ea12a1](facebook@4ea12a1)
1 parent 92282b2 commit 1180f1b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+19378
-20684
lines changed

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

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,8 @@ __DEV__ &&
185185
-1 < x.stack.indexOf("\n at")
186186
? " (<anonymous>)"
187187
: -1 < x.stack.indexOf("@")
188-
? "@unknown:0:0"
189-
: "";
188+
? "@unknown:0:0"
189+
: "";
190190
}
191191
return "\n" + prefix + name + suffix;
192192
}
@@ -598,13 +598,15 @@ __DEV__ &&
598598
null === type
599599
? (isStaticChildren = "null")
600600
: isArrayImpl(type)
601-
? (isStaticChildren = "array")
602-
: void 0 !== type && type.$$typeof === REACT_ELEMENT_TYPE
603-
? ((isStaticChildren =
604-
"<" + (getComponentNameFromType(type.type) || "Unknown") + " />"),
605-
(children =
606-
" Did you accidentally export a JSX literal instead of a component?"))
607-
: (isStaticChildren = typeof type);
601+
? (isStaticChildren = "array")
602+
: void 0 !== type && type.$$typeof === REACT_ELEMENT_TYPE
603+
? ((isStaticChildren =
604+
"<" +
605+
(getComponentNameFromType(type.type) || "Unknown") +
606+
" />"),
607+
(children =
608+
" Did you accidentally export a JSX literal instead of a component?"))
609+
: (isStaticChildren = typeof type);
608610
error(
609611
"React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",
610612
isStaticChildren,
@@ -638,11 +640,7 @@ __DEV__ &&
638640
hasValidKey(config) &&
639641
(checkKeyStringCoercion(config.key), (children = "" + config.key));
640642
hasValidRef(config) && warnIfStringRefCannotBeAutoConverted(config, self);
641-
if (
642-
(!enableFastJSXWithoutStringRefs &&
643-
(!enableFastJSXWithStringRefs || "ref" in config)) ||
644-
"key" in config
645-
) {
643+
if ("ref" in config || "key" in config) {
646644
maybeKey = {};
647645
for (var propName in config)
648646
"key" !== propName &&
@@ -813,7 +811,6 @@ __DEV__ &&
813811
disableDefaultPropsExceptForClasses =
814812
dynamicFeatureFlags.disableDefaultPropsExceptForClasses,
815813
enableDebugTracing = dynamicFeatureFlags.enableDebugTracing,
816-
enableFastJSX = dynamicFeatureFlags.enableFastJSX,
817814
enableRenderableContext = dynamicFeatureFlags.enableRenderableContext,
818815
enableTransitionTracing = dynamicFeatureFlags.enableTransitionTracing,
819816
renameElementSymbol = dynamicFeatureFlags.renameElementSymbol,
@@ -867,9 +864,7 @@ __DEV__ &&
867864
specialPropKeyWarningShown;
868865
var didWarnAboutStringRefs = {};
869866
var didWarnAboutElementRef = {};
870-
var enableFastJSXWithStringRefs = enableFastJSX && !0,
871-
enableFastJSXWithoutStringRefs = enableFastJSXWithStringRefs && !1,
872-
didWarnAboutKeySpread = {},
867+
var didWarnAboutKeySpread = {},
873868
ownerHasKeyUseWarning = {};
874869
exports.Fragment = REACT_FRAGMENT_TYPE;
875870
exports.jsxDEV = function (

compiled/facebook-www/JSXDEVRuntime-dev.modern.js

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,8 @@ __DEV__ &&
185185
-1 < x.stack.indexOf("\n at")
186186
? " (<anonymous>)"
187187
: -1 < x.stack.indexOf("@")
188-
? "@unknown:0:0"
189-
: "";
188+
? "@unknown:0:0"
189+
: "";
190190
}
191191
return "\n" + prefix + name + suffix;
192192
}
@@ -595,13 +595,15 @@ __DEV__ &&
595595
null === type
596596
? (isStaticChildren = "null")
597597
: isArrayImpl(type)
598-
? (isStaticChildren = "array")
599-
: void 0 !== type && type.$$typeof === REACT_ELEMENT_TYPE
600-
? ((isStaticChildren =
601-
"<" + (getComponentNameFromType(type.type) || "Unknown") + " />"),
602-
(children =
603-
" Did you accidentally export a JSX literal instead of a component?"))
604-
: (isStaticChildren = typeof type);
598+
? (isStaticChildren = "array")
599+
: void 0 !== type && type.$$typeof === REACT_ELEMENT_TYPE
600+
? ((isStaticChildren =
601+
"<" +
602+
(getComponentNameFromType(type.type) || "Unknown") +
603+
" />"),
604+
(children =
605+
" Did you accidentally export a JSX literal instead of a component?"))
606+
: (isStaticChildren = typeof type);
605607
error(
606608
"React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",
607609
isStaticChildren,
@@ -635,11 +637,7 @@ __DEV__ &&
635637
hasValidKey(config) &&
636638
(checkKeyStringCoercion(config.key), (children = "" + config.key));
637639
hasValidRef(config) && warnIfStringRefCannotBeAutoConverted(config, self);
638-
if (
639-
(!enableFastJSXWithoutStringRefs &&
640-
(!enableFastJSXWithStringRefs || "ref" in config)) ||
641-
"key" in config
642-
) {
640+
if ("ref" in config || "key" in config) {
643641
maybeKey = {};
644642
for (var propName in config)
645643
"key" !== propName &&
@@ -810,7 +808,6 @@ __DEV__ &&
810808
disableDefaultPropsExceptForClasses =
811809
dynamicFeatureFlags.disableDefaultPropsExceptForClasses,
812810
enableDebugTracing = dynamicFeatureFlags.enableDebugTracing,
813-
enableFastJSX = dynamicFeatureFlags.enableFastJSX,
814811
enableRenderableContext = dynamicFeatureFlags.enableRenderableContext,
815812
enableTransitionTracing = dynamicFeatureFlags.enableTransitionTracing;
816813
dynamicFeatureFlags = dynamicFeatureFlags.renameElementSymbol;
@@ -863,9 +860,7 @@ __DEV__ &&
863860
specialPropKeyWarningShown;
864861
var didWarnAboutStringRefs = {};
865862
var didWarnAboutElementRef = {};
866-
var enableFastJSXWithStringRefs = enableFastJSX && !0,
867-
enableFastJSXWithoutStringRefs = enableFastJSXWithStringRefs && !1,
868-
didWarnAboutKeySpread = {},
863+
var didWarnAboutKeySpread = {},
869864
ownerHasKeyUseWarning = {};
870865
exports.Fragment = REACT_FRAGMENT_TYPE;
871866
exports.jsxDEV = function (

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0117239720b6ea830376f4f8605957ccae8b3735
1+
4ea12a11d1848c1398f9a8babcfbcd51e150f1d9
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0117239720b6ea830376f4f8605957ccae8b3735
1+
4ea12a11d1848c1398f9a8babcfbcd51e150f1d9

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

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,8 @@ __DEV__ &&
272272
-1 < x.stack.indexOf("\n at")
273273
? " (<anonymous>)"
274274
: -1 < x.stack.indexOf("@")
275-
? "@unknown:0:0"
276-
: "";
275+
? "@unknown:0:0"
276+
: "";
277277
}
278278
return "\n" + prefix + name + suffix;
279279
}
@@ -660,13 +660,15 @@ __DEV__ &&
660660
null === type
661661
? (isStaticChildren = "null")
662662
: isArrayImpl(type)
663-
? (isStaticChildren = "array")
664-
: void 0 !== type && type.$$typeof === REACT_ELEMENT_TYPE
665-
? ((isStaticChildren =
666-
"<" + (getComponentNameFromType(type.type) || "Unknown") + " />"),
667-
(children =
668-
" Did you accidentally export a JSX literal instead of a component?"))
669-
: (isStaticChildren = typeof type);
663+
? (isStaticChildren = "array")
664+
: void 0 !== type && type.$$typeof === REACT_ELEMENT_TYPE
665+
? ((isStaticChildren =
666+
"<" +
667+
(getComponentNameFromType(type.type) || "Unknown") +
668+
" />"),
669+
(children =
670+
" Did you accidentally export a JSX literal instead of a component?"))
671+
: (isStaticChildren = typeof type);
670672
error$jscomp$0(
671673
"React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",
672674
isStaticChildren,
@@ -700,11 +702,7 @@ __DEV__ &&
700702
hasValidKey(config) &&
701703
(checkKeyStringCoercion(config.key), (children = "" + config.key));
702704
hasValidRef(config) && warnIfStringRefCannotBeAutoConverted(config, self);
703-
if (
704-
(!enableFastJSXWithoutStringRefs &&
705-
(!enableFastJSXWithStringRefs || "ref" in config)) ||
706-
"key" in config
707-
) {
705+
if ("ref" in config || "key" in config) {
708706
maybeKey = {};
709707
for (var propName in config)
710708
"key" !== propName &&
@@ -1202,7 +1200,6 @@ __DEV__ &&
12021200
disableDefaultPropsExceptForClasses =
12031201
dynamicFeatureFlags.disableDefaultPropsExceptForClasses,
12041202
enableDebugTracing = dynamicFeatureFlags.enableDebugTracing,
1205-
enableFastJSX = dynamicFeatureFlags.enableFastJSX,
12061203
enableRenderableContext = dynamicFeatureFlags.enableRenderableContext,
12071204
enableTransitionTracing = dynamicFeatureFlags.enableTransitionTracing,
12081205
renameElementSymbol = dynamicFeatureFlags.renameElementSymbol,
@@ -1319,9 +1316,7 @@ __DEV__ &&
13191316
didWarnAboutOldJSXRuntime;
13201317
var didWarnAboutStringRefs = {};
13211318
var didWarnAboutElementRef = {};
1322-
var enableFastJSXWithStringRefs = enableFastJSX && !0,
1323-
enableFastJSXWithoutStringRefs = enableFastJSXWithStringRefs && !1,
1324-
didWarnAboutKeySpread = {},
1319+
var didWarnAboutKeySpread = {},
13251320
ownerHasKeyUseWarning = {},
13261321
didWarnAboutMaps = !1,
13271322
userProvidedKeyEscapeRegex = /\/+/g,
@@ -1688,13 +1683,13 @@ __DEV__ &&
16881683
isArrayImpl(type)
16891684
? (typeString = "array")
16901685
: void 0 !== type && type.$$typeof === REACT_ELEMENT_TYPE
1691-
? ((typeString =
1692-
"<" +
1693-
(getComponentNameFromType(type.type) || "Unknown") +
1694-
" />"),
1695-
(i =
1696-
" Did you accidentally export a JSX literal instead of a component?"))
1697-
: (typeString = typeof type);
1686+
? ((typeString =
1687+
"<" +
1688+
(getComponentNameFromType(type.type) || "Unknown") +
1689+
" />"),
1690+
(i =
1691+
" Did you accidentally export a JSX literal instead of a component?"))
1692+
: (typeString = typeof type);
16981693
error$jscomp$0(
16991694
"React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",
17001695
typeString,
@@ -1769,18 +1764,18 @@ __DEV__ &&
17691764
"forwardRef requires a render function but received a `memo` component. Instead of forwardRef(memo(...)), use memo(forwardRef(...))."
17701765
)
17711766
: "function" !== typeof render
1772-
? error$jscomp$0(
1773-
"forwardRef requires a render function but was given %s.",
1774-
null === render ? "null" : typeof render
1775-
)
1776-
: 0 !== render.length &&
1777-
2 !== render.length &&
1778-
error$jscomp$0(
1779-
"forwardRef render functions accept exactly two parameters: props and ref. %s",
1780-
1 === render.length
1781-
? "Did you forget to use the ref parameter?"
1782-
: "Any additional parameter will be undefined."
1783-
);
1767+
? error$jscomp$0(
1768+
"forwardRef requires a render function but was given %s.",
1769+
null === render ? "null" : typeof render
1770+
)
1771+
: 0 !== render.length &&
1772+
2 !== render.length &&
1773+
error$jscomp$0(
1774+
"forwardRef render functions accept exactly two parameters: props and ref. %s",
1775+
1 === render.length
1776+
? "Did you forget to use the ref parameter?"
1777+
: "Any additional parameter will be undefined."
1778+
);
17841779
null != render &&
17851780
null != render.defaultProps &&
17861781
error$jscomp$0(
@@ -1926,6 +1921,14 @@ __DEV__ &&
19261921
exports.unstable_useCacheRefresh = function () {
19271922
return resolveDispatcher().useCacheRefresh();
19281923
};
1924+
exports.unstable_useContextWithBailout = function (context, select) {
1925+
var dispatcher = resolveDispatcher();
1926+
context.$$typeof === REACT_CONSUMER_TYPE &&
1927+
error$jscomp$0(
1928+
"Calling useContext(Context.Consumer) is not supported and will cause bugs. Did you mean to call useContext(Context) instead?"
1929+
);
1930+
return dispatcher.unstable_useContextWithBailout(context, select);
1931+
};
19291932
exports.unstable_useMemoCache = useMemoCache;
19301933
exports.use = function (usable) {
19311934
return resolveDispatcher().use(usable);
@@ -1998,7 +2001,7 @@ __DEV__ &&
19982001
exports.useTransition = function () {
19992002
return resolveDispatcher().useTransition();
20002003
};
2001-
exports.version = "19.0.0-www-classic-01172397-20240716";
2004+
exports.version = "19.0.0-www-classic-4ea12a11-20240801";
20022005
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
20032006
"function" ===
20042007
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

0 commit comments

Comments
 (0)