Aller au contenu principal

What do the 'behavior' attribute on KeyboardAvoidingView ?

· 5 minutes de lecture

This article explains in depth the bahavior of the KeyboardAvoidingView attribute: behavior

What is the 'behavior' attribute on KeyboardAvoidingView ?

Introduction

React Native is a framework that have been in the landscape for many years (since 2015) and is widely used 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

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:

    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:

    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:

    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:

    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 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:
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.