react-native
react-native copied to clipboard
Image onLayout + measure race condition
Description
- Calling the measure method on React.Image within the onLayout hook of said component gives inconsistent results.
- On first rendering of the screen containing the Image the dimensions returned by measure are correct. On subsequent rerendering of the screen the returned width and height are a few pixels less than the true values.
- Workaround: add a delay of 300ms before calling measure.
- I feel like the component is measured before the screen transition animation has fully finished because the screen "grows into view". However adding "animationEnabled: false" to my navigator screen options didn't help. (The animation still looks the same).
Version
0.66.4
Output of npx react-native info
System: OS: Linux 4.19 Debian GNU/Linux 10 (buster) 10 (buster) CPU: (4) x64 Intel(R) Core(TM) i7-4600U CPU @ 2.10GHz Memory: 1.57 GB / 11.61 GB Shell: 5.0.3 - /bin/bash Binaries: Node: 12.22.7 - /usr/bin/node Yarn: Not Found npm: 6.14.15 - /usr/bin/npm Watchman: Not Found SDKs: Android SDK: Not Found IDEs: Android Studio: Not Found Languages: Java: 11.0.12 - /usr/bin/javac npmPackages: @react-native-community/cli: Not Found react: 17.0.2 => 17.0.2 react-native: 0.66.4 => 0.66.4 npmGlobalPackages: react-native: Not Found
Steps to reproduce
I don't have a complete code example just now but here is the situation:
- The Image component lives an a stack navigator screen
- The Image component has fixed width and height
- The Image component is centered within a flexbox View
- The onLayout hook of the image component looks somewhat like this:
setRectangle = ({nativeEvent}) => {
setTimeout( // <-- workaround
() => {
this.imgRef.current.measure((x, y, w, h, px, py) => {
console.log(w, h);
});
});
}, 300
);
}
- Without the timeout the reported dimensions are inconsistent.
Snack, code example, screenshot, or link to a repository
No response
I independently ran into this same issue. I couldn't figure out why measure
and measureInWindow
were giving inconsistent results. Wrapping them in setTimeout(..., 300)
results in the correct dimensions. The View I'm getting dimensions for is inside a React Modal.
Platform: IOS 15.1.1, iPhone 13 mini React 17.0.1 React Native 0.64.3 Expo 43
Update: I think I'm getting this race condition because my View is in a Modal which slides up from the bottom of the screen. Depending on when measureInWindow
is called, the View will be in a different page position.
I am still struggling with this issue. What is the right time to call the Image.measure method? It kind of works with the 300ms delay in Image.props.onLayout, however it doesn't feel very clean and occasionally I run into the Image ref being null or width and height of the image being zero...
After three months is finally found a better way to do things :)
I guess this isn't getting many views but on the off chance that someone with the same problem comes across this thread, I found a workaround for my particular use case: I want to track whether a TouchMove has moved outside the receving View or not. This is absurdly tricky because the TouchMove event gives coordinates relative to the currently hovered component but doesn't reveal whether this is still the component where the TouchMove began or whether it has moved to another. So I have to use the absolute coordinates (nativeEvent.pageX, nativeEvent.pageY) and compare them to absolute layout of my View, which again is tricky to come by other than by the disgusting workaround mentioned above. It is possible though. :) Here is what I do now:
const AmazingSelfAwareTouchable = () => {
const positionRef = useRef();
const handleResponder = ({nativeEvent: e}) => {
// This is how we get the absolute position of the View:
positionRef.current = {
left: e.pageX - e.locationX,
top: e.pageY - e.locationY
};
return true;
};
const update = ({nativeEvent: {pageX: x, pageY: y}}) => {
if(!positionRef.current) { return; }
x -= positionRef.current.left;
y -= positionRef.current.top;
console.log('Position relative to View even when moving outside the View:', x,y);
};
return (
<View
onMoveShouldSetResponder = {handleResponder}
onTouchStart = {update}
onResponderMove = {update}
/>
);
};
@ react native dev: While the original issue is probably a messy android issue i wanna suggest to provide an easier way to tell whether a TouchMove has traveled outside the original component which should be straight forward to implement.
This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.
This issue was closed because it has been stalled for 7 days with no activity.