入力欄と直近発言が画面下にあるようなチャットアプリにおいて、iOSのバーチャルキーボードが被って不都合が起きていた

解決方法

  • 基本方針はwindow.scrollTo
  • ネットワークのレスポンスでログが増える
    • コンテンツが予測不能なタイミングで非同期に更新され、高さが変わる
    • React.useEffectはコンポーネントのレンダリング後に呼ばれる、これを使う
      • const [logs, setLogs] = useState(...)
      • レスポンスでsetLogsして再描画をトリガーする
    • useEffect(scrollToBottom, [logs])でレンダリング後にスクロール
    • これでこの経路は解決
  • テキストエリアが中身の文章量によってサイズを変える
    • Material-UITextareaAutosize
    • onChangeでスクロールしても上手くいかなかった
      • なぜなら、このタイミングではまだレンダリングが終わっていないから
      • TextareaAutosize
        • 内容変更時に高さを調べて、更新する必要があればsetStateしている
        • この変更のトリガーでコンポーネントのレンダリングが走る
      • 子コンポーネントのレンダリングの後にフックをする方法がわからなかった
        • setTimeout(scrollToBottom);する
        • これでレンダリング後になる保証はないかも
  • scrollToBottomの中身 ts
const scrollToBottom = () => {
  const e = document.getElementById("bottom") as HTMLElement;
  const y = e.offsetTop - document.documentElement.clientHeight + 300;
  if (y > 0) {
    window.scrollTo(0, y);
  }
};
- 300はバーチャルキーボードの高さ。デバイスから取得する方法がわからなかった
    - 参考資料(*1)によれば301で、手元での11ProMaxでの実験でもまあ問題なさそうだったからこうした
- bottomは入力欄の下にhrを置いてる

https://github.com/mui-org/material-ui/blob/next/packages/material-ui/src/TextareaAutosize/TextareaAutosize.js pKeicho

window.innerHeightで取得する場合、上にスクロールをしてアドレスバーが表示されているときと、下にスクロールしてアドレスバーが表示されていないときでは取得できる値が異なる。 document.documentElement.clientHeightで取得する場合はアドレスバーによる影響はなく、常に一定の値が取得できる。

virtual keyboard