Skip to content
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

Dynamic bottomOffset not respected when switching between inputs #743

Open
Tobbe opened this issue Dec 19, 2024 · 4 comments
Open

Dynamic bottomOffset not respected when switching between inputs #743

Tobbe opened this issue Dec 19, 2024 · 4 comments
Assignees
Labels
🐛 bug Something isn't working KeyboardAwareScrollView 📜 Anything related to KeyboardAwareScrollView component repro provided Issue contains reproduction repository/code

Comments

@Tobbe
Copy link

Tobbe commented Dec 19, 2024

Describe the bug
When I switch focus from one input to another and change the bottom offset used for my <KeyboardAwareScrollView> it doesn't seem to actually use the new offset. You can see in the code snippet below how I change the offset when I focus the second input. If I focus the second input directly everything works. But if I first focus the top input, and then go directly to focus the second one the offset seems to stay at 30

Code snippet

This is obviously just parts of the code. See the linked repo for the full implementation

export default function AwareScrollView() {
  const [bottomOffset, setBottomOffset] = useState(30);

  return (
    <KeyboardAwareScrollView
      bottomOffset={bottomOffset}
      contentContainerStyle={styles.content}
    >
        <TextInput /* ... */ />

        <TextInput
          // ...
          onBlur={(e) => {
            setBottomOffset(30);
          }}
          onFocus={(e) => {
            setBottomOffset(90);
          }}
        />
    </KeyboardAwareScrollView>
  );
}

Repo for reproducing
https://github.com/Tobbe/rnkc-bottom-offset

To Reproduce
Steps to reproduce the behavior:

  1. Run the app in the repo above
  2. Press the password input field. Notice how the full button is visible above the keyboard
  3. Press the lorem ipsum text to dismiss the keyboard
  4. Press the email input field
  5. Press the password input filed.
  6. Notice how the full button is not visible

Expected behavior

I expect the full button to always be visible after switching focus to the password input field

Screenshots
Here's a video of the behavior I'm seeing

Screen.Recording.2024-12-19.at.16.01.22.mov

Smartphone (please complete the following information):

  • Desktop OS: Latest MacOS
  • Device: Android Emulator
  • OS: Android 15
  • RN version: 0.76.5
  • RN architecture: old
  • JS engine: Hermes
  • Library version: 1.15.1

Additional context

In this particular example I can just keep the offset to always be 90. But with more input fields, or just more text above the first one, always doing 90 (or more) feels like too much

Another workaround that kind of works is to also change the bottom offset when the text changes. So as soon as the user starts typing in the password input field the log in button scrolls into view.

onChangeText={(text) => {
  setBottomOffset(90);
  setPassword(text);
}}
@kirillzyusko kirillzyusko added 🐛 bug Something isn't working KeyboardAwareScrollView 📜 Anything related to KeyboardAwareScrollView component labels Dec 19, 2024
@kirillzyusko
Copy link
Owner

@Tobbe thank you for raising this issue 🙌

let me think a little bit about this problem and about potential ways to solve that and I'll get back to you.

One solution I've been thinking of is putting maybeScroll into useEffect, something like this:

useEffect(() => {
  maybeScroll(keyboardHeight.value);
}, [bottomOffset]);

But I just thinking whether such method can bring additional complexity or not and undesired bugs (pretty sure it will).

Another solution I'm thinking of is exposing a method from a ref (like assureTargetViewVisible/scrollToMakeViewVisible/etc.), so that in your code you can imperatively call this method on demand (when you need to be sure that view is not overlapped by keyboard).

And I think the second approach requires more hand-engineering (which is bad), but at the same time it's more generic approach (for example if you have validation errors that shift layout down - in this case I can not detect such layout changes, so it can be kind of developer responsibility to call that method on ref whenever errors/screen re-draw happens).

Curious to hear your thoughts 👀

@Tobbe
Copy link
Author

Tobbe commented Dec 19, 2024

for example if you have validation errors that shift layout down

This is a good point! I haven't thought much about that yet. Let me play around a bit with error and how I display them in my app and see what I come up with

@ppakseev
Copy link

Do you think this could be caused by maybeScroll not being passed as a hook dependency? I'm facing kind of the same issue now, where bottomOffset is changing dynamically.

From what I see, this hook picks up the maybeScroll function only at the very first renderer, and although subsequent changes to values ​​recreate the function, it is not (passed to / updated in) this hook.

src/components/KeyboardAwareScrollView/index.tsx

    useAnimatedReaction(
      () => input.value,
      (current, previous) => {
        if (
          current?.target === previous?.target &&
          current?.layout.height !== previous?.layout.height
        ) {
          const prevLayout = layout.value;

          layout.value = input.value;
          scrollPosition.value += maybeScroll(keyboardHeight.value, true);
          layout.value = prevLayout;
        }
      },
      [],
    );

@kirillzyusko
Copy link
Owner

useAnimatedReaction(
      () => input.value,
      (current, previous) => {
        if (
          current?.target === previous?.target &&
          current?.layout.height !== previous?.layout.height
        ) {
          const prevLayout = layout.value;

          layout.value = input.value;
          scrollPosition.value += maybeScroll(keyboardHeight.value, true);
          layout.value = prevLayout;
        }
      },
      [],
    );

I think this code should detect only "input grow" event (at least it was originally designed to do that) 🤔

Input switching gets handled in useSmoothKeyboardHandler. And if you change bottomOffset, then maybeScroll gets called first (when input was changed via worklet) with not yet updated bottomOffset value. I think it's what causing the bug 👀

@kirillzyusko kirillzyusko added the repro provided Issue contains reproduction repository/code label Dec 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐛 bug Something isn't working KeyboardAwareScrollView 📜 Anything related to KeyboardAwareScrollView component repro provided Issue contains reproduction repository/code
Projects
None yet
Development

No branches or pull requests

3 participants