Closed kirillzyusko closed 3 days ago
Current size | Target Size | Difference |
---|---|---|
144688 bytes | 144183 bytes | 505 bytes 📈 |
@kirillzyusko hello! 👋 I was wondering if the new Reanimated hook useComposedEventHandler
could be helpful in this case for merging scroll handlers together, just like this:
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 onScroll
from KeyboardAwareScrollView
is not fired 🙈
And I think useComposedEventHandler
gives an access only to nativeEvent
, am I right?
in this case onScroll from KeyboardAwareScrollView is not fired 🙈
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
. Considering that KeyboardAwareScrollView
is built on top of Animated.ScrollView
(https://github.com/kirillzyusko/react-native-keyboard-controller/blob/main/src/components/KeyboardAwareScrollView/index.tsx#L343), it maybe should accept both formats.
onScroll
→ function callback<KeyboardAwareScrollView
onScroll={(e) => console.log('FUNCTION', e)}
scrollEventThrottle={16}
>
{children}
</KeyboardAwareScrollView>
2.onScroll
→ scroll handler from Reanimated
const scrollHandler = useAnimatedScrollHandler({
onScroll: (event) => {
console.log('SCROLL HANDLER', event);
},
});
<KeyboardAwareScrollView
onScroll={scrollHandler}
scrollEventThrottle={16}
>
{children}
</KeyboardAwareScrollView>
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 onScroll
prop:
@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 scrollHandler
and implementation will look like:
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 KeyboardAwareScrollView
into Reanimated.createAnimatedComponent
and pass your instance of useAnimatedScrollHandler
to your component returned by Reanimated.createAnimatedComponent
(but I doubt it will work).
I hope I understand your intention properly, but if not - feel free to correct me and explain again what you are trying to achieve 😊
do I correctly understand that you want to attach your own reanimated scroll handler?
correct!
In this case probably you'll need to pass another property scrollHandler and implementation will look like
I would keep it as a single prop (onScroll
) instead of having two (onScroll
and scrollHandler
). At least this is how it works when using Animated.ScrollView
: you can pass either a function or a scroll handler to onScroll
Another option would be to try to wrap KeyboardAwareScrollView into Reanimated.createAnimatedComponent
I have tried this before, and it does work, but the performance seems slower, especially on Android devices
📜 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 https://github.com/software-mansion/react-native-reanimated/issues/6204), so I'm always re-dispatching
nativeEvent
manually viarunOnJS
.The issue was caught originally in https://github.com/kirillzyusko/react-native-keyboard-controller/pull/488
📢 Changelog
JS
🤔 How Has This Been Tested?
TextInput#3
Tested on Android 12 (Pixel 2) with disabled animations (detox).
📸 Screenshots (if appropriate):
📝 Checklist