How does the 'behavior' property of the KeyboardAvoidingView component works ?
Tags: reactnative, mobile
Published: 2023-02-22 10:22:51


React Native is a framework that have been in the landscape for many years (since 2015) and is [widely used](https://reactnative.dev/showcase) today. 
This fame brings several challenges, one of them is the Developer Experience (DX for short). DX partly relies on the **documentation**: a bad one can
prevent the framework adoption. So, one job of the maintainers is to update/improve it with the help or not of the community. 

About React Native, one of the documentation flows I noticed regards The _'KeyboardAvoidingView'_ component and the _'behavior'_ attribute. 
The documentation is quite terse on the subject unfortunately. That's the purpose of this article: trying to clarify the behavior of the attribute.
What does it do ? For that we'll look at the source code.

_Disclaimer: you can find the source code [here](https://github.com/facebook/react-native/blob/92073d4a71d50a1ed80cf9cb063a6144fcc8cf19/Libraries/Components/Keyboard/KeyboardAvoidingView.js#L157)_

## Overview

The behavior props is an enum of 3 values:
- height
- position
- padding

And as it is a facultative prop, a default behavior is implemented.

Each one adapts the position of the children components following the keyboard dimensions using a specific method. Using the _'height'_ css atribute or the _'padding'_ one for instance.
The _'position'_ value acts differently and nor accordingly from its name, ie, doesn't use the 'position' css attribute. 

Let's show some code ! All the logic is contained in the _'render'_ method and a **switch case**. 

### height

Here is an extract of the source code: 

```jsx
    case 'height':
        let heightStyle;
        if (this._frame != null && this.state.bottom > 0) {
          heightStyle = {
            height: this._initialFrameHeight - bottomHeight,
            flex: 0,
          };
        }
        return (
          <View
            ref={this.viewRef}
            style={StyleSheet.compose(
              style,
              heightStyle,
            )}
            onLayout={this._onLayout}
            {...props}>
            {children}
          </View>
        );
```

In this case, we check if the keyboard is present via the `bottom` state variable (a state variable containing the height of the keyboard). And if yes, we create a view whose `style` has a defined `height` attribute equals to the substraction of
the frame heigth and the size of the keyboard. Also, a `flex: 0` is set, meaning that the `View` just takes the place defined by its heigth, no extra calculus done to make it bigger or smaller.
The condition is present to make the heigth revert if the user closes the keyboard, otherwise it would have been stucked.

So, this behavior is equivalent to create a `View` component with a fixed defined height. 

### padding

Here is an extract of the source code: 

```jsx
    case 'padding':
        return (
            <View
                    ref={this.viewRef}
                    style={StyleSheet.compose(
                            style,
                            {paddingBottom: bottomHeight},
                    )}
                    onLayout={this._onLayout}
                    {...props}>
              {children}
            </View>
        );
```

For this behavior, the framework creates a `View` component, as the previous one but this time we play with the `paddingBottom` attribute, ie the component enlarges itself from the bottom 
by a value defined by a `bottomHeigth`, which is the height of the keyboard. So, we push the content of the view above to make place for the keyboard by making the container larger.

So, this behavior is equivalent to create a `View` component with a bottom padding large enough to welcome the keyboard.

### position

Here is an extract of the source code:

```jsx
    case 'position':
        return (
            <View
                    ref={this.viewRef}
                    style={style}
                    onLayout={this._onLayout}
                    {...props}>
              <View
                      style={StyleSheet.compose(
                              contentContainerStyle,
                              {
                                bottom: bottomHeight,
                              },
                      )}>
                {children}
              </View>
            </View>
        );
```

This one differs from the previous. Indeed, in this case the `View` is not enlarged but placed higher in the screen regardless of the other components. Its position is absolute. 
Moreover, we create 2 nested views, one with the props and style and the other with the newly applied property plus another style provided by the `contentContainerStyle` property.

So, this behavior is equivalent to create two `Views` component with an absolute placement of the size of the keyboard.

### default

Here is an extract of the source code:

```jsx
    default:
        return (
            <View
                    ref={this.viewRef}
                    onLayout={this._onLayout}
                    style={style}
                    {...props}>
              {children}
            </View>
        );
```

As you can see, no extra styling are added so we fallback into the default configuration. It behaves differently according the OS:
- in android, it tries to adjust the size of the screen to let place to the keyboard thanks to the `adjustResize` attribute defined by default in the manifest
- in ios, the view doesn't resizes so the keyboard overlaps the input. Indeed, by default, no attributes exists and some [code](https://betterprogramming.pub/how-to-adjust-views-to-fit-the-keyboard-in-ios-63bcf42163b8)
is needed to do that

So this behavior lets the OS places the keyboard natively.

## Conclusion

Unfortunately, we can't predict exactly the behavior according the OS and they both 
interact with this prop differently.

That's why we recommend: 
1. To set this prop and act as it is a mandatory one, by wrapping it with a component making it required. 
An example:

```typescript
import {KeyboardAvoidingView as RNKeyboardAvoidingView} from 'react-native';
import {KeyboardAvoidingViewProps} from 'react-native/Libraries/Components/Keyboard/KeyboardAvoidingView';

interface KeyboardAvoidingViewType extends KeyboardAvoidingViewProps {}

const KeyboardAvoidingView = ({
                                behavior,
                                children,
                                ...props
                              }: KeyboardAvoidingViewType) => {
  return (
          <RNKeyboardAvoidingView behavior={behavior} {...props}>
            {children}
          </RNKeyboardAvoidingView>
  );
};

export default KeyboardAvoidingView;
```

2.To trial and error with the different possibilities, and not hesitating to change the value following the OS.
For instance, in our test case (a simple text input emulated on a ios 14 and pixel xl), only the _height_
attribute worked for and android and the other two only for ios.