main
js 105 lines 3.28 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10 import type {ReactContext} from 'shared/ReactTypes';
11
12 import * as React from 'react';
13 import {createContext, useContext, useEffect, useState} from 'react';
14 import {
15 BridgeContext,
16 StoreContext,
17 } from 'react-devtools-shared/src/devtools/views/context';
18 import {TreeStateContext} from 'react-devtools-shared/src/devtools/views/Components/TreeContext';
19
20 import type {StateContext} from 'react-devtools-shared/src/devtools/views/Components/TreeContext';
21 import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
22 import type Store from 'react-devtools-shared/src/devtools/store';
23 import type {StyleAndLayout as StyleAndLayoutBackend} from 'react-devtools-shared/src/backend/NativeStyleEditor/types';
24 import type {StyleAndLayout as StyleAndLayoutFrontend} from './types';
25
26 type Context = StyleAndLayoutFrontend | null;
27
28 const NativeStyleContext: ReactContext<Context> = createContext<Context>(
29 null as any as Context,
30 );
31 NativeStyleContext.displayName = 'NativeStyleContext';
32
33 type Props = {
34 children: React$Node,
35 };
36
37 function NativeStyleContextController({children}: Props): React.Node {
38 const bridge = useContext<FrontendBridge>(BridgeContext);
39 const store = useContext<Store>(StoreContext);
40 const {inspectedElementID} = useContext<StateContext>(TreeStateContext);
41
42 const [currentStyleAndLayout, setCurrentStyleAndLayout] =
43 useState<StyleAndLayoutFrontend | null>(null);
44
45 // This effect handler polls for updates on the currently selected element.
46 useEffect(() => {
47 if (inspectedElementID === null) {
48 setCurrentStyleAndLayout(null);
49 return () => {};
50 }
51
52 let requestTimeoutId: TimeoutID | null = null;
53 const sendRequest = () => {
54 requestTimeoutId = null;
55 const rendererID = store.getRendererIDForElement(inspectedElementID);
56
57 if (rendererID !== null) {
58 bridge.send('NativeStyleEditor_measure', {
59 id: inspectedElementID,
60 rendererID,
61 });
62 }
63 };
64
65 // Send the initial measurement request.
66 // We'll poll for an update in the response handler below.
67 sendRequest();
68
69 const onStyleAndLayout = ({id, layout, style}: StyleAndLayoutBackend) => {
70 // If this is the element we requested, wait a little bit and then ask for another update.
71 if (id === inspectedElementID) {
72 if (requestTimeoutId !== null) {
73 clearTimeout(requestTimeoutId);
74 }
75 requestTimeoutId = setTimeout(sendRequest, 1000);
76 }
77
78 const styleAndLayout: StyleAndLayoutFrontend = {
79 layout,
80 style,
81 };
82 setCurrentStyleAndLayout(styleAndLayout);
83 };
84
85 bridge.addListener('NativeStyleEditor_styleAndLayout', onStyleAndLayout);
86 return () => {
87 bridge.removeListener(
88 'NativeStyleEditor_styleAndLayout',
89 onStyleAndLayout,
90 );
91
92 if (requestTimeoutId !== null) {
93 clearTimeout(requestTimeoutId);
94 }
95 };
96 }, [bridge, inspectedElementID, store]);
97
98 return (
99 <NativeStyleContext.Provider value={currentStyleAndLayout}>
100 {children}
101 </NativeStyleContext.Provider>
102 );
103 }
104
105 export {NativeStyleContext, NativeStyleContextController};