Skip to content

Commit b765799

Browse files
committed
fix api
1 parent 40a7f48 commit b765799

File tree

2 files changed

+136
-95
lines changed

2 files changed

+136
-95
lines changed

src/core/instrumentation/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ export const instrument = ({
140140
onCommitStart();
141141

142142
const handleFiber = (fiber: Fiber, trigger: boolean) => {
143+
// to-do, don't traverse fibers part from react-scan, get all components from no-traverse subtree and put it in a weakset
143144
const type = getType(fiber.type);
144145
if (!type) return null;
145146
if (!didFiberRender(fiber)) return null;

src/core/native/index.tsx

Lines changed: 135 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,17 @@ import {
1515
Text,
1616
} from '@shopify/react-native-skia';
1717
import React, {
18+
createContext,
19+
useContext,
1820
useEffect,
1921
useRef,
2022
useState,
21-
useSyncExternalStore,
2223
} from 'react';
23-
// import { useDerivedValue, useSharedValue } from 'react-native-reanimated';
2424
import { ReactScanInternals } from '..';
25+
import { useSharedValue, withTiming } from 'react-native-reanimated';
2526
import { assertNative, instrumentNative } from './instrument';
2627

27-
// can't use useSyncExternalStore for compat
28+
// can't use useSyncExternalStore for back compat
2829
const useIsPaused = () => {
2930
const [isPaused, setIsPaused] = useState(ReactScanInternals.isPaused);
3031
useEffect(() => {
@@ -36,18 +37,76 @@ const useIsPaused = () => {
3637
return isPaused;
3738
};
3839

39-
export const ReactNativeScanEntryPoint = () => {
40-
if (ReactScanInternals.isProd) {
41-
return null; // todo: better no-op
42-
}
40+
interface Options {
41+
/**
42+
* Controls the animation of the re-render overlay.
43+
* When set to "fade-out", the overlay will fade out after appearing.
44+
* When false, no animation will be applied.
45+
* Note: Enabling animations may impact performance.
46+
* @default false */
47+
animationWhenFlashing?: 'fade-out' | false;
48+
}
4349

50+
export type ReactNativeScanOptions = Required<Options> &
51+
Omit<
52+
typeof ReactScanInternals.options,
53+
'playSound' | 'runInProduction' | 'includeChildren'
54+
>;
55+
56+
const OptionsContext = createContext<ReactNativeScanOptions>({
57+
animationWhenFlashing: false,
58+
});
59+
60+
export const ReactScan = ({
61+
children,
62+
...options
63+
}: {
64+
children: React.ReactNode;
65+
} & ReactNativeScanOptions) => {
4466
useEffect(() => {
45-
if (!ReactScanInternals.isProd) {
46-
instrumentNative(); // cleanup?
47-
}
67+
ReactScanInternals.options = options;
68+
instrumentNative();
4869
}, []);
4970
const isPaused = useIsPaused();
5071

72+
useEffect(() => {
73+
const interval = setInterval(() => {
74+
if (isPaused) return;
75+
76+
const newActive = ReactScanInternals.activeOutlines.filter(
77+
(x) => Date.now() - x.updatedAt < 500,
78+
);
79+
if (newActive.length !== ReactScanInternals.activeOutlines.length) {
80+
ReactScanInternals.set('activeOutlines', newActive);
81+
}
82+
}, 200);
83+
return () => {
84+
clearInterval(interval);
85+
};
86+
}, [isPaused]);
87+
88+
return (
89+
<>
90+
{children}
91+
<OptionsContext.Provider value={options}>
92+
{!isPaused && <ReactScanCanvas scanTag="react-scan-no-traverse" />}
93+
{options.showToolbar && (
94+
<ReactScanToolbar
95+
scanTag="react-scan-no-traverse"
96+
isPaused={isPaused}
97+
/>
98+
)}
99+
</OptionsContext.Provider>
100+
</>
101+
);
102+
};
103+
104+
const ReactScanToolbar = ({
105+
isPaused,
106+
}: {
107+
isPaused: boolean;
108+
scanTag: string;
109+
}) => {
51110
const pan = useRef(new Animated.ValueXY()).current;
52111

53112
const panResponder = useRef(
@@ -70,77 +129,57 @@ export const ReactNativeScanEntryPoint = () => {
70129
}),
71130
).current;
72131

73-
useEffect(() => {
74-
const interval = setInterval(() => {
75-
if (isPaused) return;
76-
77-
const newActive = ReactScanInternals.activeOutlines.filter(
78-
(x) => Date.now() - x.updatedAt < 500,
79-
);
80-
if (newActive.length !== ReactScanInternals.activeOutlines.length) {
81-
ReactScanInternals.set('activeOutlines', newActive);
82-
}
83-
}, 200);
84-
return () => {
85-
clearInterval(interval);
86-
};
87-
}, [isPaused]);
88-
89132
return (
90-
<>
91-
{!isPaused && <ReactNativeScan id="react-scan-no-traverse" />}
92-
93-
<Animated.View
94-
id="react-scan-no-traverse"
133+
<Animated.View
134+
id="react-scan-no-traverse"
135+
style={{
136+
position: 'absolute',
137+
bottom: 20,
138+
right: 20,
139+
zIndex: 999999,
140+
transform: pan.getTranslateTransform(),
141+
}}
142+
{...panResponder.panHandlers}
143+
>
144+
<Pressable
145+
onPress={() =>
146+
(ReactScanInternals.isPaused = !ReactScanInternals.isPaused)
147+
}
95148
style={{
96-
position: 'absolute',
97-
bottom: 20,
98-
right: 20,
99-
zIndex: 999999,
100-
transform: pan.getTranslateTransform(),
149+
backgroundColor: !isPaused
150+
? 'rgba(88, 82, 185, 0.9)'
151+
: 'rgba(88, 82, 185, 0.5)',
152+
paddingHorizontal: 12,
153+
paddingVertical: 6,
154+
borderRadius: 4,
155+
flexDirection: 'row',
156+
alignItems: 'center',
157+
gap: 8,
101158
}}
102-
{...panResponder.panHandlers}
103159
>
104-
<Pressable
105-
onPress={() =>
106-
(ReactScanInternals.isPaused = !ReactScanInternals.isPaused)
107-
}
160+
<View
108161
style={{
109-
backgroundColor: !isPaused
110-
? 'rgba(88, 82, 185, 0.9)'
111-
: 'rgba(88, 82, 185, 0.5)',
112-
paddingHorizontal: 12,
113-
paddingVertical: 6,
162+
width: 8,
163+
height: 8,
114164
borderRadius: 4,
115-
flexDirection: 'row',
116-
alignItems: 'center',
117-
gap: 8,
165+
backgroundColor: !isPaused ? '#4ADE80' : '#666',
166+
}}
167+
/>
168+
<RNText
169+
style={{
170+
color: 'white',
171+
fontSize: 14,
172+
fontWeight: 'bold',
173+
fontFamily: Platform.select({
174+
ios: 'Courier',
175+
default: 'monospace',
176+
}),
118177
}}
119178
>
120-
<View
121-
style={{
122-
width: 8,
123-
height: 8,
124-
borderRadius: 4,
125-
backgroundColor: !isPaused ? '#4ADE80' : '#666',
126-
}}
127-
/>
128-
<RNText
129-
style={{
130-
color: 'white',
131-
fontSize: 14,
132-
fontWeight: 'bold',
133-
fontFamily: Platform.select({
134-
ios: 'Courier',
135-
default: 'monospace',
136-
}),
137-
}}
138-
>
139-
React Scan
140-
</RNText>
141-
</Pressable>
142-
</Animated.View>
143-
</>
179+
React Scan
180+
</RNText>
181+
</Pressable>
182+
</Animated.View>
144183
);
145184
};
146185
const dimensions = Dimensions.get('window');
@@ -155,24 +194,30 @@ const font = matchFont({
155194
const getTextWidth = (text: string) => {
156195
return (text || 'unknown').length * 7;
157196
};
158-
const ReactNativeScan = ({ id: _ }: { id: string }) => {
159-
// const opacity = useSharedValue(1);
160-
// todo: polly fill
161-
const outlines = useSyncExternalStore(
162-
(listener) =>
163-
ReactScanInternals.subscribe('activeOutlines', (_) => {
164-
// animations destroy UI thread on heavy updates, probably not worth it
165-
// opacity.value = 1;
166-
// opacity.value = withTiming(0, {
167-
// duration: 500
168-
// })
169-
listener();
170-
}),
171-
() => ReactScanInternals.activeOutlines,
172-
);
173-
// );
174-
// const animatedOpacity = useDerivedValue(() => opacity.value);
175197

198+
const useOutlines = (opacity: { value: number }) => {
199+
const [outlines, setOutlines] = useState<
200+
(typeof ReactScanInternals)['activeOutlines']
201+
>([]);
202+
const options = useContext(OptionsContext);
203+
// cannot use useSyncExternalStore for back compat
204+
useEffect(() => {
205+
ReactScanInternals.subscribe('activeOutlines', (activeOutlines) => {
206+
setOutlines(activeOutlines);
207+
if (options.animationWhenFlashing !== false) {
208+
// we only support fade-out for now
209+
opacity.value = 1;
210+
opacity.value = withTiming(0, {
211+
duration: 500,
212+
});
213+
}
214+
});
215+
}, []);
216+
return outlines;
217+
};
218+
const ReactScanCanvas = (_: { scanTag: string }) => {
219+
const opacity = useSharedValue(1);
220+
const outlines = useOutlines(opacity);
176221
return (
177222
<Canvas
178223
style={{
@@ -255,8 +300,3 @@ const ReactNativeScan = ({ id: _ }: { id: string }) => {
255300
</Canvas>
256301
);
257302
};
258-
259-
260-
261-
262-

0 commit comments

Comments
 (0)