Skip to content

Commit d379ff5

Browse files
authored
fix: KeyboardAvoidingView + autoFocus + native-stack (#880)
## 📜 Description Fixed incorrect `KeyboardAvoidingView` layout when it's used together with input (`autoFocus`) in `native-stack`. ## 💡 Motivation and Context The problem happens because of this: > The problem here is that onLayout get triggered two times (with 852 and 722 values). The correct value is 722 (because view doesn't occupy all screen, the header above takes ~80px). > > The problem is that when onLayout with 722 gets triggered, then keyboard is already shown and we don't take this event into consideration, because that event may come from the fact, that layout of the view was changed because of KeyboardAvoidingView transformation I added condition `initialFrame.value === null` in the beginning of implementation (we also discussed potential but in https://github.com/kirillzyusko/react-native-keyboard-controller/pull/229/files#r1318714840) After that I changed condition to `keyboard.isClosed.value` in #239 And after ~6 months I added `initialFrame.value === null` again in #391 So the current condition looks like "update layout only when keyboard closed or if layout is unknown". While it works it may produce issues as in the description above. (i. e. if keyboard opens, and first layout is incorrect, then resize will be incorrect too). I though a lot on how to fix it, and realized, that layout actually is not changing if we are using `padding`, `bottom` or `translate-with-padding` modes. So technically that condition was added only for `behavior==="height"`. After thinking a while I decided, that the best fix here at the moment will be to add additional `behavior !== "height"` condition. It fixes the problem and theoretically it shouldn't have any negative impact for all 3 modes, as they do not change layout when keyboard is visible. If that condition indeed works and I will not receive new bug reports, then we can modify the condition for `height` as well and remove `keyboard.isClosed.value || initialFrame.value === null` totally (in this case, when we receive `onLayout` with `height` mode we will have to add `relativeKeyboardHeight` to layout event to exclude the keyboard frame from layout event 😎). Closes #759 ## 📢 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 - added `behavior !== "height"` condition to `onLayout` callback (to update layout whenever it's fired for all modes excluding `height`); ## 🤔 How Has This Been Tested? - tested in reproduction example; - tested example app; - tests passed in CI. In manual testing I used ## 📸 Screenshots (if appropriate): |Before|After|RN| |------|------|--| |<video src="https://github.com/user-attachments/assets/bd4cb792-6bbe-49ba-b38b-7fe9eb031e05">|<video src="https://github.com/user-attachments/assets/60f72417-6c56-4ea1-be1f-ad4009f90c37">|<video src="https://github.com/user-attachments/assets/7588aca8-2bdc-4d53-bffa-88f5891034b8">| ## 📝 Checklist - [x] CI successfully passed - [x] I added new mocks and corresponding unit-tests if library API was changed
1 parent 87e47e5 commit d379ff5

1 file changed

Lines changed: 14 additions & 7 deletions

File tree

src/components/KeyboardAvoidingView/index.tsx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,21 @@ const KeyboardAvoidingView = forwardRef<
106106
[relativeKeyboardHeight],
107107
);
108108

109-
const onLayoutWorklet = useCallback((layout: LayoutRectangle) => {
110-
"worklet";
109+
const onLayoutWorklet = useCallback(
110+
(layout: LayoutRectangle) => {
111+
"worklet";
111112

112-
if (keyboard.isClosed.value || initialFrame.value === null) {
113-
// eslint-disable-next-line react-compiler/react-compiler
114-
initialFrame.value = layout;
115-
}
116-
}, []);
113+
if (
114+
keyboard.isClosed.value ||
115+
initialFrame.value === null ||
116+
behavior !== "height"
117+
) {
118+
// eslint-disable-next-line react-compiler/react-compiler
119+
initialFrame.value = layout;
120+
}
121+
},
122+
[behavior],
123+
);
117124
const onLayout = useCallback<NonNullable<ViewProps["onLayout"]>>(
118125
(e) => {
119126
runOnUI(onLayoutWorklet)(e.nativeEvent.layout);

0 commit comments

Comments
 (0)