Skip to content

Commit

Permalink
docs: comparison with useAnimatedKeyboard (#567)
Browse files Browse the repository at this point in the history
## 📜 Description

Added a note about `useAnimatedKeyboard` and this library difference. 

## 💡 Motivation and Context

To keep people aware on high level what is the difference between
implementation.

In future I may revisit the table and make some adjustments - it's only
basic work.

Closes
#561

## 📢 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 -->

### Docs

- added a note about simultaneous usage of `useAnimatedKeyboard` and
this library;
- add comparison table for `useAnimatedKeyboard` and this library.

## 🤔 How Has This Been Tested?

Tested manually on and via preview.

## 📝 Checklist

- [x] CI successfully passed
- [x] I added new mocks and corresponding unit-tests if library API was
changed
  • Loading branch information
kirillzyusko authored Aug 26, 2024
1 parent 6a2332b commit 26f54e0
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 63 deletions.
4 changes: 3 additions & 1 deletion cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@
"Raphson",
"desmos",
"swmansion",
"rnscreens"
"rnscreens",
"setinputmode",
"useanimatedkeyboard"
],
"ignorePaths": [
"node_modules",
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/guides/first-animation.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,5 @@ export default function KeyboardAnimation() {
```

:::info
If you are going to use these Animated values in class components (i.e. without hooks) - you can easily [do](../api/hooks/keyboard/use-keyboard-animation.md) it. Check out [source](https://github.com/kirillzyusko/react-native-keyboard-controller/blob/cf27eb00877db34b860a04cf52a026110e44b4b3/src/animated.tsx#L46-L51) code - this hook simply changes `softInputMode` and consumes `Context`. Also you may read [architecture](../recipes/architecture.md) deep dive to understand more about how this library works.
If you are going to use these Animated values in class components (i.e. without hooks) - you can easily [do](../api/hooks/keyboard/use-keyboard-animation.md) it. Check out [source](https://github.com/kirillzyusko/react-native-keyboard-controller/blob/cf27eb00877db34b860a04cf52a026110e44b4b3/src/animated.tsx#L46-L51) code - this hook simply changes `softInputMode` and consumes `Context`. Also you may read [architecture](../recipes/architecture.mdx) deep dive to understand more about how this library works.
:::
6 changes: 6 additions & 0 deletions docs/docs/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ npm install react-native-keyboard-controller --save
This library requires `react-native-reanimated` to work properly. If you don't have it in your project, you need to follow [installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/#installation) and install it in your project before using this library.
:::

:::info Usage with `useAnimatedKeyboard` hook from `react-native-reanimated`
`react-native-keyboard-controller` and `useAnimatedKeyboard` hook (from `react-native-reanimated`) _**may**_ technically be used together, but it's **highly recommended** to use only one of them to avoid any kind of conflicts.

If you want to know the difference between `useAnimatedKeyboard` and the implementation of this library, please read [this comparison](./recipes/architecture#what-is-the-difference-between-useanimatedkeyboard-from-react-native-reanimated-and-this-library).
:::

### Linking

This package supports [autolinking](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md).
Expand Down
30 changes: 0 additions & 30 deletions docs/docs/recipes/architecture.md

This file was deleted.

66 changes: 66 additions & 0 deletions docs/docs/recipes/architecture.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
sidebar_position: 1
keywords: [react-native-keyboard-controller, architecture, design principles]
---

# Architecture

This library requires to wrap an app with `KeyboardProvider` component. It's needed because it stores animated values in `context`.

## Process overview

Library exposes `KeyboardControllerView` with `onKeyboardMove` method. This method is fired when keyboard frame is changed. `KeyboardProvider` automatically maps these events to `Animated.Value` and `Reanimated.SharedValue` and stores it in `context`.

:::info
Under the hood `KeyboardControllerView` is a simple `View` with one additional `onKeyboardMove` callback method, so it inherits all props from plain `View`, such as `style`, etc.
:::

Thus we have a single source of truth about keyboard position. Since values are stored in `context` we can use it in any component where we need them. Moreover, we can consume `context` values in class components as well as in hooks.

## Design principles

The library was designed to use a `context` as a global store for animated values and have a single `Provider` across the app. As of now it may be not very obvious, why it was needed to have a single source of data flow, but in future it may significantly simplify the process of the integration new features.

## Why custom `KeyboardControllerView` is needed?

Initially I had a choice which approach to use in order to send events about keyboard frames: `EventEmitters` vs `View` with callbacks. I decided to use `View` with callbacks because of several reasons:

- `react-native` core team uses similar approach for `onScroll` event from `ScrollView` component (also I knew, that it's possible to map events from such callbacks to `Animated.Value` and thus reduce bridge usage);
- to track keyboard frames on Android we need to enter to [edge-to-edge](https://developer.android.com/training/gestures/edge-to-edge) mode and it changes view paddings. Since it's managed through `View` it's easier to change padding of this view.
- `reanimated` allows to intercept `view` events using theirs `useEvent` hook and move the event handling into worklet runtime. Thus sending events via `view` allows to make an integration with `reanimated` package and handle events/animate everything directly on the UI thread.

## What is the difference between `useAnimatedKeyboard` from `react-native-reanimated` and this library?

`react-native-keyboard-controller` uses its own implementation for keyboard handling and leverages `react-native-reanimated` solely for performing UI thread updates using `SharedValue` (the library doesn't simply re-export `useAnimatedKeyboard` hook in any kind of form).

While both `useAnimatedKeyboard` from `react-native-reanimated` and this library aims to provide the same functionality, there are some differences between them. Below you can find a comparison of the two libraries:

<!-- prettier-ignore-start -->
| |`react-native-keyboard-controller`|`react-native-reanimated`|
|-|----------------------------------|-------------------------|
|Map keyboard movement to animated value|✅|✅|
|Synchronously update keyboard position on UI thread|✅|✅|
|Dynamically switch [`softInputMode`](../api//keyboard-controller.md#setinputmode-)|✅|❌|
|An ability to turn functionality on demand|✅|🟠 <sup><small>1</small></sup>|
|Android interactive keyboard support|✅|❌|
|iOS interactive keyboard support|✅|✅|
|Has pre-built components|✅|❌|
|Works in `Modal` on Android|✅|🟠 <sup><small>2</small></sup>||
|Is ready-to-use library for keyboard avoidance<sup><small>3</small></sup>|✅|❌|
|`KeyboardToolbar` component|✅|❌|
<!-- prettier-ignore-end -->

> <sup>1</sup> You need to unmount all components that use `useAnimatedKeyboard`
> to disable module functionality, which can be hard to achieve if you are using
> deep Stack-navigators.
> <sup>2</sup> Planned to be added in the future
> <sup>3</sup> The `react-native-keyboard-controller` tracks focused input changes
> (apart of keyboard tracking) and thus brings advanced concepts for keyboard avoidance.
To sum it up:

- if you are using `useAnimatedKeyboard` and you are satisfied with it, then there is no sense to switch to `react-native-keyboard-controller`;

- if you are planning to add advanced keyboard handling into large existing project, then `react-native-keyboard-controller` can be a better choice, since it has drop-in replacement components (`KeyboardAvoidingView`, `KeyboardAwareScrollView`, etc.), you can toggle the functionality dynamically on per screen basic, you can dynamically change `softInputMode` which should simplify the integration process.
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,5 @@ export default function KeyboardAnimation() {
```

:::info
If you are going to use these Animated values in class components (i.e. without hooks) - you can easily [do](../api/hooks/keyboard/use-keyboard-animation.md) it. Check out [source](https://github.com/kirillzyusko/react-native-keyboard-controller/blob/cf27eb00877db34b860a04cf52a026110e44b4b3/src/animated.tsx#L46-L51) code - this hook simply changes `softInputMode` and consumes `Context`. Also you may read [architecture](../recipes/architecture.md) deep dive to understand more about how this library works.
If you are going to use these Animated values in class components (i.e. without hooks) - you can easily [do](../api/hooks/keyboard/use-keyboard-animation.md) it. Check out [source](https://github.com/kirillzyusko/react-native-keyboard-controller/blob/cf27eb00877db34b860a04cf52a026110e44b4b3/src/animated.tsx#L46-L51) code - this hook simply changes `softInputMode` and consumes `Context`. Also you may read [architecture](../recipes/architecture.mdx) deep dive to understand more about how this library works.
:::
6 changes: 6 additions & 0 deletions docs/versioned_docs/version-1.13.0/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ npm install react-native-keyboard-controller --save
This library requires `react-native-reanimated` to work properly. If you don't have it in your project, you need to follow [installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/#installation) and install it in your project before using this library.
:::

:::info Usage with `useAnimatedKeyboard` hook from `react-native-reanimated`
`react-native-keyboard-controller` and `useAnimatedKeyboard` hook (from `react-native-reanimated`) _**may**_ technically be used together, but it's **highly recommended** to use only one of them to avoid any kind of conflicts.

If you want to know the difference between `useAnimatedKeyboard` and the implementation of this library, please read [this comparison](./recipes/architecture#what-is-the-difference-between-useanimatedkeyboard-from-react-native-reanimated-and-this-library).
:::

### Linking

This package supports [autolinking](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md).
Expand Down
30 changes: 0 additions & 30 deletions docs/versioned_docs/version-1.13.0/recipes/architecture.md

This file was deleted.

66 changes: 66 additions & 0 deletions docs/versioned_docs/version-1.13.0/recipes/architecture.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
sidebar_position: 1
keywords: [react-native-keyboard-controller, architecture, design principles]
---

# Architecture

This library requires to wrap an app with `KeyboardProvider` component. It's needed because it stores animated values in `context`.

## Process overview

Library exposes `KeyboardControllerView` with `onKeyboardMove` method. This method is fired when keyboard frame is changed. `KeyboardProvider` automatically maps these events to `Animated.Value` and `Reanimated.SharedValue` and stores it in `context`.

:::info
Under the hood `KeyboardControllerView` is a simple `View` with one additional `onKeyboardMove` callback method, so it inherits all props from plain `View`, such as `style`, etc.
:::

Thus we have a single source of truth about keyboard position. Since values are stored in `context` we can use it in any component where we need them. Moreover, we can consume `context` values in class components as well as in hooks.

## Design principles

The library was designed to use a `context` as a global store for animated values and have a single `Provider` across the app. As of now it may be not very obvious, why it was needed to have a single source of data flow, but in future it may significantly simplify the process of the integration new features.

## Why custom `KeyboardControllerView` is needed?

Initially I had a choice which approach to use in order to send events about keyboard frames: `EventEmitters` vs `View` with callbacks. I decided to use `View` with callbacks because of several reasons:

- `react-native` core team uses similar approach for `onScroll` event from `ScrollView` component (also I knew, that it's possible to map events from such callbacks to `Animated.Value` and thus reduce bridge usage);
- to track keyboard frames on Android we need to enter to [edge-to-edge](https://developer.android.com/training/gestures/edge-to-edge) mode and it changes view paddings. Since it's managed through `View` it's easier to change padding of this view.
- `reanimated` allows to intercept `view` events using theirs `useEvent` hook and move the event handling into worklet runtime. Thus sending events via `view` allows to make an integration with `reanimated` package and handle events/animate everything directly on the UI thread.

## What is the difference between `useAnimatedKeyboard` from `react-native-reanimated` and this library?

`react-native-keyboard-controller` uses its own implementation for keyboard handling and leverages `react-native-reanimated` solely for performing UI thread updates using `SharedValue` (the library doesn't simply re-export `useAnimatedKeyboard` hook in any kind of form).

While both `useAnimatedKeyboard` from `react-native-reanimated` and this library aims to provide the same functionality, there are some differences between them. Below you can find a comparison of the two libraries:

<!-- prettier-ignore-start -->
| |`react-native-keyboard-controller`|`react-native-reanimated`|
|-|----------------------------------|-------------------------|
|Map keyboard movement to animated value|✅|✅|
|Synchronously update keyboard position on UI thread|✅|✅|
|Dynamically switch [`softInputMode`](../api//keyboard-controller.md#setinputmode-)|✅|❌|
|An ability to turn functionality on demand|✅|🟠 <sup><small>1</small></sup>|
|Android interactive keyboard support|✅|❌|
|iOS interactive keyboard support|✅|✅|
|Has pre-built components|✅|❌|
|Works in `Modal` on Android|✅|🟠 <sup><small>2</small></sup>||
|Is ready-to-use library for keyboard avoidance<sup><small>3</small></sup>|✅|❌|
|`KeyboardToolbar` component|✅|❌|
<!-- prettier-ignore-end -->

> <sup>1</sup> You need to unmount all components that use `useAnimatedKeyboard`
> to disable module functionality, which can be hard to achieve if you are using
> deep Stack-navigators.
> <sup>2</sup> Planned to be added in the future
> <sup>3</sup> The `react-native-keyboard-controller` tracks focused input changes
> (apart of keyboard tracking) and thus brings advanced concepts for keyboard avoidance.
To sum it up:

- if you are using `useAnimatedKeyboard` and you are satisfied with it, then there is no sense to switch to `react-native-keyboard-controller`;

- if you are planning to add advanced keyboard handling into large existing project, then `react-native-keyboard-controller` can be a better choice, since it has drop-in replacement components (`KeyboardAvoidingView`, `KeyboardAwareScrollView`, etc.), you can toggle the functionality dynamically on per screen basic, you can dynamically change `softInputMode` which should simplify the integration process.

0 comments on commit 26f54e0

Please sign in to comment.