I have the following hook:
export default function useOverlay(action: Action, cutoutSize: number) {
const anchor = useRef<View>(null)
const { width, height } = Dimensions.get("window");
const [overlay, setOverlay] = useReducer(
mergeObjReducer<ScanOverlayProps>,
{
overlayWidth: width,
overlayHeight: height,
cutoutOffsetX: 32,
cutoutOffsetY: 0,
cutoutSize,
overlayColor: "#151515",
cutoutVisible: true
}
)
useLayoutEffect(() => {
anchor.current?.measureInWindow((x, y, w, h) => {
console.log("LAYOUT", x, y, w, h) //ERROR all values are always 0.
setOverlay({ cutoutOffsetY: y })
});
}, [])
useEffect(() => {
const isScan = (action === "scan");
setOverlay({
cutoutVisible: (isScan && overlay.cutoutOffsetY !== 0),
overlayColor: isScan ? "#151515d1" : "#151515"
})
}, [action, overlay.cutoutOffsetY])
return { anchor, overlay }
}
Used in the following component like so:
export type Action = "scan" | "qr";
export default function Actions() {
const isActive = (name: Action) => action === name;
const { scanner, data } = useCodeScanner()
const [action, setAction] = useState<Action>("qr");
const { anchor, overlay } = useOverlay(action, 240);
const active = useCameraActive(action, 50);
useActionBrightness(action);
useScanAction(data);
return (
<View style={styles.container}>
{active && <CameraView
// onError={handleCameraError}
isActive
codeScanner={scanner}
style={styles.camera}
/>}
<Overlay {...overlay} />
<View style={styles.svgContent}>
<View ref={anchor} style={styles.actionContent}>
{isActive("qr") && <DynamicQRCode size={240} data={{ actionType: "person" }} />}
</View>
</View>
</View>
)
}
Yet, whenever I log the value of any of the measure callback parameters, I always get 0. WHY? I have tried everything, delays, interactionsManager, passing a onlayout function with a ref etc. Nothing gives me the actual screen position. How do I fix this? I have been going mad for the past 4 hours....
FYI (not sure if relevant though), this screen is located in a TopTabsNavigator from react native navigation.
Let me know if any more information would be useful.
Update Overlay Hook as:
import { useRef, useReducer, useEffect, useState, useCallback } from "react";
import { Dimensions, View, LayoutChangeEvent, ViewStyle } from "react-native";
import { useFocusEffect } from "@react-navigation/native";
export default function useOverlay(action: Action, cutoutSize: number) {
const anchor = useRef<View>(null);
const layoutReady = useRef(false);
const [screenFocused, setScreenFocused] = useState(false);
const { width, height } = Dimensions.get("window");
const [overlay, setOverlay] = useReducer(mergeObjReducer<ScanOverlayProps>, {
overlayWidth: width,
overlayHeight: height,
cutoutOffsetX: 32,
cutoutOffsetY: 0,
cutoutSize,
overlayColor: "#151515",
cutoutVisible: true,
});
const onLayout = useCallback((e: LayoutChangeEvent) => {
layoutReady.current = true;
tryMeasure();
}, []);
useFocusEffect(
useCallback(() => {
setScreenFocused(true);
tryMeasure();
return () => setScreenFocused(false);
}, [])
);
const tryMeasure = () => {
if (layoutReady.current && screenFocused) {
anchor.current?.measureInWindow((x, y, w, h) => {
console.log("MEASURED", x, y, w, h);
if (y !== 0) {
setOverlay({ cutoutOffsetY: y });
}
});
}
};
useEffect(() => {
const isScan = action === "scan";
setOverlay({
cutoutVisible: isScan && overlay.cutoutOffsetY !== 0,
overlayColor: isScan ? "#151515d1" : "#151515",
});
}, [action, overlay.cutoutOffsetY]);
return { anchor, overlay, onLayout };
}
And also Actions component:
export default function Actions() {
const [action, setAction] = useState<Action>("qr");
const { scanner, data } = useCodeScanner();
const { anchor, overlay, onLayout } = useOverlay(action, 240);
const active = useCameraActive(action, 50);
useActionBrightness(action);
useScanAction(data);
const isActive = (name: Action) => action === name;
return (
<View style={styles.container}>
{active && (
<CameraView
isActive
codeScanner={scanner}
style={styles.camera}
/>
)}
<Overlay {...overlay} />
<View style={styles.svgContent}>
<View ref={anchor} onLayout={onLayout} style={styles.actionContent}>
{isActive("qr") && (
<DynamicQRCode size={240} data={{ actionType: "person" }} />
)}
</View>
</View>
</View>
);
}