software-mansion / react-native-screens

Native navigation primitives for your React Native app.
MIT License
3.01k stars 510 forks source link

enableFreeze seems to prevent KeyboardAvoidingView from updating correctly #1218

Open megantaylor opened 2 years ago

megantaylor commented 2 years ago

Description

Screen A and Screen B are two tabs in a react-navigation bottom tabs navigator.

When switching from Screen A to Screen B after opening and closing the keyboard on Screen A, the KeyboardAvoidingView on Screen B has bottom padding as though the keyboard was still open.

This happens somewhat inconsistently, like every other time I switch.

It seems as though KBAV is not recognizing that the keyboard was closed, or not updating the bottom padding size correctly.

Screenshots

Actual Behavior:

Simulator_Screen_Shot_-_iPhone_SE__2nd_generation__-_2021-11-19_at_14_22_03

Expected Behavior:

Simulator Screen Shot - iPhone SE (2nd generation) - 2021-11-19 at 15 02 34

Steps To Reproduce

  1. navigate to Screen A, focus text input to open keyboard
  2. tap on some other element to close the keyboard
  3. navigate to Screen B
  4. about every other time i repeat this sequence, i see the bottom padding on Screen B, even though the keyboard is closed.

Expected behavior

KeyboardAvoidingView should not have bottom padding on Screen B

Actual behavior

KeyboardAvoidingView has bottom padding on Screen B

Platform

Workflow

Package versions

package version
react-native 0.64.3
@react-navigation/native 5.9.8
react-native-screens 3.9.0
react-native-safe-area-context 3.3.2
react-native-gesture-handler 1.10.3
react-native-reanimated 2.2.4
expo 43.0.2
github-actions[bot] commented 2 years ago

Issue validator

The issue is valid!

kacperkapusciak commented 2 years ago

Hi @megantaylor, Thanks for submitting an issue!

I can't seem to reproduce this from the given steps. Could you please provide a simple self-contained snippet of code that reproduces the problem?

Cheers

jparkrr commented 2 years ago

Hi @kacperkapusciak, thanks for the reply! (I work with @megantaylor)

I've reproduced the issue here: https://github.com/jparkrr/repro-kav-bug

Here's a video. Keep an eye on whether the contents of tab two's KeyboardAvoidingView is vertically centered, as it should be based on the styles.

https://user-images.githubusercontent.com/872456/143946892-8bd6e395-48a6-4293-bbb4-4381075850c7.mp4

chungweileong94 commented 2 years ago

I'm actually facing the same issue before, but at the time I don't have time to submit an issue.

So basically the issue in my case was:

  1. Having KeyboardAvoidingView in both screen 1 & screen 2,
  2. Then navigate from screen 1 to screen 2
  3. Tap on input on screen 2 to bring up the keyboard
  4. Then navigate back to screen 1, and you will notice that the KeyboardAvoidingView will be stuck in the opening state on screen 1

However, the issue only happens on iOS.

ujjwalsayami commented 2 years ago

I'm facing the same issue as mentioned by @chungweileong94

megantaylor commented 2 years ago

any movement on this issue? we'd love to use reactFreeze in our app, but can't until there's a solution for this problem.

owinter86 commented 2 years ago

Not sure how easily this is addressed with react-native-screens/react-native-freeze, but I have a working solution that requires a minor patch to react native KeyBoardAvoidingView

diff --git a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
index 26897f1..831bb90 100644
--- a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
+++ b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
@@ -104,7 +104,7 @@ class KeyboardAvoidingView extends React.Component<Props, State> {
   };

   _updateBottomIfNecesarry = () => {
-    if (this._keyboardEvent == null) {
+    if (this._keyboardEvent == null || !this.props.enabled) {
       this.setState({bottom: 0});
       return;
     }

and using useIsFocused hook on the actual KeyboardAvoidingView component.

import { useIsFocused } from "@react-navigation/native";
...
const isFocussed = useIsFocused();
return (
  <KeyboardAvoidingView enabled={isFocussed}>
    ...
  </KeyboardAvoidingView>
);

That way the delayed onLayout view changes wont be triggered on the frozen screens when returning.

rpander93 commented 2 years ago

also ran into this issue recently. workaround from @owinter86 solved it for now

RichardLindhout commented 1 year ago

Another option is providing a key to the KeyboardAvoidingView

      <KeyboardAvoidingView
        key={isFocused ? 'focused' : 'not-focused'}

This will force a React re-render of the keyboardAvoidingView :)

cjshearer commented 1 year ago

Another option is providing a key to the KeyboardAvoidingView

      <KeyboardAvoidingView
        key={isFocused ? 'focused' : 'not-focused'}

This will force a React re-render of the keyboardAvoidingView :)

Triggering a re-render of a keyboard avoiding view on every navigation hurts performance too much (at least for us). @owinter86's patch is far more performant.

RichardLindhout commented 1 year ago

@cjshearer interesting! I only use KeyboardAvoidingView in screens where it is needed so it does not hurt me as much. How did you measure this?

cjshearer commented 1 year ago

@cjshearer interesting! I only use KeyboardAvoidingView in screens where it is needed so it does not hurt me as much. How did you measure this?

We're trying to use the KeyboardAvoidingView in a generic ScreenLayout component that wraps each of our screens to avoid duplication, but I see how your approach would make re-rendering on navigation far more palatable.

As for performance measuring, it was purely qualitative. The only thing "measured" was my patience as several seconds went by per-navigation 🙃. Granted, that was in debug mode, but it I can't imagine that would translate well into a release build (and development would still be torture).

DreamakerDimas commented 8 months ago

Not sure how easily this is addressed with react-native-screens/react-native-freeze, but I have a working solution that requires a minor patch to react native KeyBoardAvoidingView

diff --git a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
index 26897f1..831bb90 100644
--- a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
+++ b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
@@ -104,7 +104,7 @@ class KeyboardAvoidingView extends React.Component<Props, State> {
   };

   _updateBottomIfNecesarry = () => {
-    if (this._keyboardEvent == null) {
+    if (this._keyboardEvent == null || !this.props.enabled) {
       this.setState({bottom: 0});
       return;
     }

and using useIsFocused hook on the actual KeyboardAvoidingView component.

import { useIsFocused } from "@react-navigation/native";
...
const isFocussed = useIsFocused();
return (
  <KeyboardAvoidingView enabled={isFocussed}>
    ...
  </KeyboardAvoidingView>
);

That way the delayed onLayout view changes wont be triggered on the frozen screens when returning.

I have this issue in specific place and places where I didn't add enabled prop become broken after this fix.

So here is my version:


-    if (this._keyboardEvent == null) {
+       if (this._keyboardEvent == null || typeof this.props.enabled === 'boolean' && !this.props.enabled) {