-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
An ability to have JS handler along with animated handler simultaneously #6204
Comments
Hey! 👋 The issue doesn't seem to contain a minimal reproduction. Could you provide a snack or a link to a GitHub repository under your username that reproduces the problem? |
cc @tomekzaw as we discussed it with you in direct messages 🙃 |
Thanks for reporting this issue, I've forwarded it internally. cc @tjzel @szydlovsky |
## 📜 Description Fixed an issue where `scrollPosition.value` is updated with out-of-date value. ## 💡 Motivation and Context <!-- Why is this change required? What problem does it solve? --> <!-- If it fixes an open issue, please link to the issue here. --> The problem happens, because without animation `onMove`/`onEnd` dispatched almost simultaneously. But js handler can not update `position.value` and in `onEnd` handler in `scrollPosition.value = position.value;` code we set `scrollPosition.value = 0`. When text input grows: ```tsx layout.value = input.value; scrollPosition.value += maybeScroll(keyboardHeight.value, true); ``` We call `maybeScroll`, but `scrollPosition.value === 0` and because of that we scroll by 16px (though we had to scroll for `actualScroll (170) + 16` but we do `0 + 16`, and because of that position is incorrect. To fix the problem `onEnd` should actually update `scrollPosition` 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 via `runOnJS`. The issue was caught originally in #488 ## 📢 Changelog <!-- High level overview of important changes --> <!-- For example: fixed status bar manipulation; added new types declarations; --> <!-- If your changes don't affect one of platform/language below - then remove this platform/language --> ### JS - fix race condition between scroll position update and keyboard movement. ## 🤔 How Has This Been Tested? - Set focus to `TextInput#3` - press enter 2 times Tested on Android 12 (Pixel 2) with disabled animations (detox). ## 📸 Screenshots (if appropriate): |Before|After| |-------|-----| |<img width="399" alt="image" src="https://github.com/kirillzyusko/react-native-keyboard-controller/assets/22820318/5511fb3e-54c3-4495-9955-13e933839941">|<img width="397" alt="image" src="https://github.com/kirillzyusko/react-native-keyboard-controller/assets/22820318/00d679ba-5986-4fb6-a41a-9c6a1944c98d">| ## 📝 Checklist - [x] CI successfully passed - [x] I added new mocks and corresponding unit-tests if library API was changed
I think the ideal solution would be to re-use I haven't thought about the exact API yet, but I think a nice solution would be to have something like that: const onScrollHandler1 = useAnimatedScrollHandler({
onScroll(e) {
console.log('Scroll handler 1 onScroll event');
},
});
const onScrollHandler2 = (e) => console.log(e);
const composedHandler = useComposedEventHandler([
onScrollHandler1,
{ onScroll: onScrollHandler2, js: true },
]);
// ...
<Animated.ScrollView onScroll={composedHandler}> And my idea how to achieve that was a modification of const handler = useEvent<Event, Context>(...
handler.hasJSHandler = hasJSHandler(handlers)
return handler;
// ... and then in AnimatedComponent we need to check - if `hasJSHandler=true` then we don't need to substitute handler with `dummyListener` and we should use composed js-handler (i. e. propagate one event through all JS handers provided in `useComposedEventHandler` Of course this is a very rough idea... If you have a better idea on how to achieve his - let me know, I'll be glad to hear that 👀 But my idea was re-using current API without bringing additional complexity to developers who is going to use that 🙈 But if you see a drawbacks in the proposed solution - let me know 👍 |
@kirillzyusko I'll try building a PoC based on your idea, I will get back to you once I have some more intel on it 👍 |
By the way - I temporarily solved the problem by using But I think that the ability to have a composed mixture of worklet/js handlers still would be amazing feature 😎 |
@kirillzyusko Hi again! Coming back to you after some digging. I have a very very early stage PoC and it seems like it may fly! I've found some limitations though:
const composedHandler = useComposedEventHandler(
[onDragHandler, onMomentumHandler],
{
onBeginDrag: (_e) => {
console.log('[JS] begin drag');
},
onMomentumBegin: (_e) => {
console.log('[JS] begin momentum');
},
}
); So, the JS handlers are being passed in a similar (if not the same) object as in Tell me what you think, I am open for suggestions 😄 |
And a sneak peak of the PoC: 347903530-f19eceb1-c3c2-43ce-a65f-ea20f5d7a672.mp4 |
I think all events that intercepted by
I think it's not a limitation, but maybe it is possible to be consistent and have the API like: const composedHandler = useComposedEventHandler(
[onDragHandler, onMomentumHandler],
[{ // <- an array of JS handlers
onBeginDrag: (_e) => {
console.log('[JS] begin drag');
},
onMomentumBegin: (_e) => {
console.log('[JS] begin momentum');
},
},
{onBeginDrag: props.onBeginDrag}]
); Could be probably convenient to have an ability to compose multiple JS handlers (for example if you want to combine a handler from props and your own). But this is just idea - will be glad to hear a feedback as well 🙂 |
@kirillzyusko Alright, thanks for the response! I'll stick to the scrolling events for now. If it comes to the API, yeah, I think I might have an idea on how to merge multiple JS handlers as well - once again I'll let you know as soon as I figure something out 😄 |
Hey @kirillzyusko I have the earliest PoC patch for you to try: |
@szydlovsky thank you for your hard work 💪 I tested changes from your PR and for me it looks like they work well 👀 (though I tested only basics scenarios) Great job 🎉 |
Ahh, just as I pushed some cool changes - now the hook should react to any changes in its argument, including JS handlers + just a single, union-typed argument. Nonetheless, here's the newest patch: rea_js_handlers_second.patch and I'm opening the feature to reviews 😄 |
@szydlovsky cool, I will try to test it tomorrow or over the weekend! |
@szydlovsky I tested and everything works well! Thank you for your hard work! |
Super good news! I'll come back to you the last time when the PR get approved and ask if it still does well 😄 |
Description
I would like to have an ability to have 2 handlers (JS + reanimated) at the same time. Before I could achieve that using next code:
But at some point of time this construction stopped to work. I think it happens because of this:
react-native-reanimated/packages/react-native-reanimated/src/createAnimatedComponent/PropsFilter.tsx
Lines 79 to 86 in 4115e83
Another option was to add such code:
But in this case some of properties (
currentTarget
,cancellable
, etc.) will be ignored.Is there a recommended way to achieve this? 👀
Steps to reproduce
Add the code from description.
Snack or a link to a repository
N/A
Reanimated version
3.12.1
React Native version
0.74.2
Platforms
Android, iOS
JavaScript runtime
Hermes
Workflow
React Native
Architecture
Paper (Old Architecture)
Build type
Debug app & dev bundle
Device
iOS simulator
Device model
iPhone 15
Acknowledgements
Yes
The text was updated successfully, but these errors were encountered: