@samitouri / QOS-React-2 / commits / fa8e3a251e

[devtools] Restore all Transitions for Tree updates (#33042)

Sebastian "Sebbie" Silbermann committed Apr 30, 2025 at 19:51 UTC fa8e3a251e7840fa220a1649dec616e72658473e
5 files changed +50 -9
packages/react-devtools-shared/src/devtools/views/Components/Element.js
+3 -1
@@ -23,6 +23,7 @@ import type {Element as ElementType} from 'react-devtools-shared/src/frontend/ty
23
24 import styles from './Element.css';
25 import Icon from '../Icon';
26 +import {useChangeOwnerAction} from './OwnersListContext';
27
28 type Props = {
29 data: ItemData,
@@ -66,9 +67,10 @@ export default function Element({data, index, style}: Props): React.Node {
67 warningCount: number,
68 }>(errorsAndWarningsSubscription);
69
70 + const changeOwnerAction = useChangeOwnerAction();
71 const handleDoubleClick = () => {
72 if (id !== null) {
71 - dispatch({type: 'SELECT_OWNER', payload: id});
73 + changeOwnerAction(id);
74 }
75 };
76
packages/react-devtools-shared/src/devtools/views/Components/OwnersListContext.js
+39 -4
@@ -13,7 +13,7 @@ import * as React from 'react';
13 import {createContext, useCallback, useContext, useEffect} from 'react';
14 import {createResource} from '../../cache';
15 import {BridgeContext, StoreContext} from '../context';
16 -import {TreeStateContext} from './TreeContext';
16 +import {TreeDispatcherContext, TreeStateContext} from './TreeContext';
17 import {backendToFrontendSerializedElementMapper} from 'react-devtools-shared/src/utils';
18
19 import type {OwnersList} from 'react-devtools-shared/src/backend/types';
@@ -70,6 +70,43 @@ type Props = {
70 children: React$Node,
71 };
72
73 +function useChangeOwnerAction(): (nextOwnerID: number) => void {
74 + const bridge = useContext(BridgeContext);
75 + const store = useContext(StoreContext);
76 + const treeAction = useContext(TreeDispatcherContext);
77 +
78 + return useCallback(
79 + function changeOwnerAction(nextOwnerID: number) {
80 + treeAction({type: 'SELECT_OWNER', payload: nextOwnerID});
81 +
82 + const element = store.getElementByID(nextOwnerID);
83 + if (element !== null) {
84 + if (!inProgressRequests.has(element)) {
85 + let resolveFn:
86 + | ResolveFn
87 + | ((
88 + result:
89 + | Promise<Array<SerializedElement>>
90 + | Array<SerializedElement>,
91 + ) => void) = ((null: any): ResolveFn);
92 + const promise = new Promise(resolve => {
93 + resolveFn = resolve;
94 + });
95 +
96 + // $FlowFixMe[incompatible-call] found when upgrading Flow
97 + inProgressRequests.set(element, {promise, resolveFn});
98 + }
99 +
100 + const rendererID = store.getRendererIDForElement(nextOwnerID);
101 + if (rendererID !== null) {
102 + bridge.send('getOwnersList', {id: nextOwnerID, rendererID});
103 + }
104 + }
105 + },
106 + [bridge, store],
107 + );
108 +}
109 +
110 function OwnersListContextController({children}: Props): React.Node {
111 const bridge = useContext(BridgeContext);
112 const store = useContext(StoreContext);
@@ -95,8 +132,6 @@ function OwnersListContextController({children}: Props): React.Node {
132 if (element !== null) {
133 const request = inProgressRequests.get(element);
134 if (request != null) {
98 - inProgressRequests.delete(element);
99 -
135 request.resolveFn(
136 ownersList.owners === null
137 ? null
@@ -129,4 +164,4 @@ function OwnersListContextController({children}: Props): React.Node {
164 );
165 }
166
132 -export {OwnersListContext, OwnersListContextController};
167 +export {OwnersListContext, OwnersListContextController, useChangeOwnerAction};
packages/react-devtools-shared/src/devtools/views/Components/OwnersStack.js
+3 -2
@@ -20,7 +20,7 @@ import Button from '../Button';
20 import ButtonIcon from '../ButtonIcon';
21 import Toggle from '../Toggle';
22 import ElementBadges from './ElementBadges';
23 -import {OwnersListContext} from './OwnersListContext';
23 +import {OwnersListContext, useChangeOwnerAction} from './OwnersListContext';
24 import {TreeDispatcherContext, TreeStateContext} from './TreeContext';
25 import {useIsOverflowing} from '../hooks';
26 import {StoreContext} from '../context';
@@ -81,6 +81,7 @@ export default function OwnerStack(): React.Node {
81 const read = useContext(OwnersListContext);
82 const {ownerID} = useContext(TreeStateContext);
83 const treeDispatch = useContext(TreeDispatcherContext);
84 + const changeOwnerAction = useChangeOwnerAction();
85
86 const [state, dispatch] = useReducer<State, State, Action>(dialogReducer, {
87 ownerID: null,
@@ -116,7 +117,7 @@ export default function OwnerStack(): React.Node {
117 type: 'UPDATE_SELECTED_INDEX',
118 selectedIndex: index >= 0 ? index : 0,
119 });
119 - treeDispatch({type: 'SELECT_OWNER', payload: owner.id});
120 + changeOwnerAction(owner.id);
121 } else {
122 dispatch({
123 type: 'UPDATE_SELECTED_INDEX',
packages/react-devtools-shared/src/devtools/views/Components/Tree.js
+3 -1
@@ -38,6 +38,7 @@ import ButtonIcon from '../ButtonIcon';
38 import Button from '../Button';
39 import {logEvent} from 'react-devtools-shared/src/Logger';
40 import {useExtensionComponentsPanelVisibility} from 'react-devtools-shared/src/frontend/hooks/useExtensionComponentsPanelVisibility';
41 +import {useChangeOwnerAction} from './OwnersListContext';
42
43 // Never indent more than this number of pixels (even if we have the room).
44 const DEFAULT_INDENTATION_SIZE = 12;
@@ -217,13 +218,14 @@ export default function Tree(): React.Node {
218 const handleBlur = useCallback(() => setTreeFocused(false), []);
219 const handleFocus = useCallback(() => setTreeFocused(true), []);
220
221 + const changeOwnerAction = useChangeOwnerAction();
222 const handleKeyPress = useCallback(
223 (event: $FlowFixMe) => {
224 switch (event.key) {
225 case 'Enter':
226 case ' ':
227 if (inspectedElementID !== null) {
226 - dispatch({type: 'SELECT_OWNER', payload: inspectedElementID});
228 + changeOwnerAction(inspectedElementID);
229 }
230 break;
231 default:
packages/react-devtools-shared/src/devtools/views/Components/TreeContext.js
+2 -1
@@ -148,6 +148,7 @@ const TreeStateContext: ReactContext<StateContext> =
148 createContext<StateContext>(((null: any): StateContext));
149 TreeStateContext.displayName = 'TreeStateContext';
150
151 +// TODO: `dispatch` is an Action and should be named accordingly.
152 const TreeDispatcherContext: ReactContext<DispatcherContext> =
153 createContext<DispatcherContext>(((null: any): DispatcherContext));
154 TreeDispatcherContext.displayName = 'TreeDispatcherContext';
@@ -953,7 +954,7 @@ function TreeContextController({
954
955 return (
956 <TreeStateContext.Provider value={state}>
956 - <TreeDispatcherContext.Provider value={dispatch}>
957 + <TreeDispatcherContext.Provider value={transitionDispatch}>
958 {children}
959 </TreeDispatcherContext.Provider>
960 </TreeStateContext.Provider>