-
-
Notifications
You must be signed in to change notification settings - Fork 64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: race condition between js and worklet #490
fix: race condition between js and worklet #490
Conversation
📊 Package size report
|
@kirillzyusko hello! 👋 I was wondering if the new Reanimated hook const internalOnScroll = useAnimatedScrollHandler(
{
onScroll: (event) => {
position.value = event.contentOffset.y;
},
},
[],
);
const onScroll = useComposedEventHandler(
[internalOnScroll, onScrollProps].filter(Boolean),
); |
@mobily I tried your code, but in this case And I think |
If you pass a function, that's the correct behavior. However, passing another scroll handler works fine and the event is fired. The current implementation causes crashes when passing a scroll handler because it's not supported internally by
<KeyboardAwareScrollView
onScroll={(e) => console.log('FUNCTION', e)}
scrollEventThrottle={16}
>
{children}
</KeyboardAwareScrollView> CleanShot.2024-07-05.at.14.05.59.mp42. const scrollHandler = useAnimatedScrollHandler({
onScroll: (event) => {
console.log('SCROLL HANDLER', event);
},
});
<KeyboardAwareScrollView
onScroll={scrollHandler}
scrollEventThrottle={16}
>
{children}
</KeyboardAwareScrollView> CleanShot.2024-07-05.at.14.09.41.mp4
The initial suggestion fails to consider the first point (a function callback). The final implementation might appear as: const internalOnScroll = useAnimatedScrollHandler(
{
onScroll: (event) => {
position.value = event.contentOffset.y;
if (typeof onScrollProps === "function") {
runOnJS(onScrollProps)({ nativeEvent: event });
}
},
},
[onScrollProps],
);
const onScroll = useComposedEventHandler([
internalOnScroll,
typeof onScrollProps === "object" ? onScrollProps : null,
]); And here's the crash/error I encounter when passing a scroll handler (from Reanimated) to the CleanShot.2024-07-05.at.14.13.52.mp4 |
@mobily do I correctly understand that you want to attach your own reanimated scroll handler? In this case probably you'll need to pass another property const internalOnScroll = useAnimatedScrollHandler(
{
onScroll: (event) => {
position.value = event.contentOffset.y;
if (typeof onScrollProps === "function") {
runOnJS(onScrollProps)({ nativeEvent: event });
}
},
},
[onScrollProps],
);
const onScroll = useComposedEventHandler([
internalOnScroll,
scrollHandler,
].filter(Boolean)); Another option would be to try to wrap I hope I understand your intention properly, but if not - feel free to correct me and explain again what you are trying to achieve 😊 |
correct!
I would keep it as a single prop (
I have tried this before, and it does work, but the performance seems slower, especially on Android devices |
hello, @kirillzyusko 👋 are you considering adding support for scroll handlers? do you want me to create a new PR with the implementation? |
Hey @mobily Sorry for being non-responsive - I was thinking about your words. I think you are totally right - you as a developer should pass one handler to I posted a proposal in reanimated - software-mansion/react-native-reanimated#6204 (comment) let's see what REA team thinks about that 👀 If they agree to support such functionality - I think we should simply wait for the new release and start to use new functionality. If they don't agree to support both types of handlers at the same time, then yes, we'll need to go for 2 props. I think in a meantime you can create a local patch (to support two types of handlers) until we get a response from REA? |
📜 Description
Fixed an issue where
scrollPosition.value
is updated with out-of-date value.💡 Motivation and Context
The problem happens, because without animation
onMove
/onEnd
dispatched almost simultaneously. But js handler can not updateposition.value
and inonEnd
handler inscrollPosition.value = position.value;
code we setscrollPosition.value = 0
.When text input grows:
We call
maybeScroll
, butscrollPosition.value === 0
and because of that we scroll by 16px (though we had to scroll foractualScroll (170) + 16
but we do0 + 16
, and because of that position is incorrect.To fix the problem
onEnd
should actually updatescrollPosition
to the expected value, and to achieve that we need to update a value from worklet.Now it's slightly problematic, because current REA doesn't support both: js + worklet handlers (see software-mansion/react-native-reanimated#6204), so I'm always re-dispatching
nativeEvent
manually viarunOnJS
.The issue was caught originally in #488
📢 Changelog
JS
🤔 How Has This Been Tested?
TextInput#3
Tested on Android 12 (Pixel 2) with disabled animations (detox).
📸 Screenshots (if appropriate):
📝 Checklist