困りごと
ReactでuseState管理されているinput要素が増えた時に、その増えたinput要素にフォーカスを当てたい、というニーズは結構あるように思います。
Androidは比較的素直かと思いますが、(少なくともiOS16.6の)iOSのSafariではフォーカスは当たるものの、仮想キーボードが表示されないという困った現象が発生します。
それを解消を目指します。
検証環境
問題があるコード
import React, { useEffect, useRef, useState } from "react"; /** サンプル用アイテム */ interface Item { id : number name : string } const App = () => { const [items, setItems] = useState<Item[]>([]) useEffect(() => { // アイテムが増えたらフォーカスする(減少にも反応するがそこは良しなに) const element = document.getElementById("input-" + items.length) if (element) { element.focus() } }, [items]) /** 追加ボタンクリックイベント */ const onClickButton = () => { setItems(old => { return [...old, { id : old.length + 1, name : "Hoge" }] }) } return <> {items.map(item => { return <div key={item.id}> <input id={"input-" + item.id} type="text"></input> </div> })} <button onClick={onClickButton} type="button">追加</button> </> }
iOSで閲覧するとわかると思いますが、フォーカスは当たるもの仮想キーボードが表示されません。
作戦
ダミーのテキストフィールドに一瞬フォーカスを当てて、フォーカスを当てたい要素に改めてフォーカスを当てる。
ダミーテキストフィールドを上手く隠せたらよかったのですが、display:noneにするとそもそもフォーカスが取れなくなるので、そこは辛いところ。
width,heightを1pxにするなど上手く隠すか、最初から表示されている何か別のテキストフィールドをダミーテキストと使うと良いかと思います。
キーボードが表示されるコード
import React, { useEffect, useRef, useState } from "react"; /** サンプル用アイテム */ interface Item { id : number name : string } const App = () => { const [items, setItems] = useState<Item[]>([]) useEffect(() => { // アイテムが増えたらフォーカスする(減少にも反応するがそこは良しなに) const element = document.getElementById("input-" + items.length) if (element) { element.focus() } }, [items]) /** 追加ボタンクリックイベント */ const onClickButton = () => { setItems(old => { // 加筆文 if (refDummyInput.current) { refDummyInput.current.focus() } // 加筆文 ここまで return [...old, { id : old.length + 1, name : "Hoge" }] }) } // 加筆部分 const refDummyInput = useRef<HTMLInputElement>(null) return <> {/* 加筆部分 */} <div> <input type="text" placeholder="ダミー" ref={refDummyInput}></input> </div> {/* 加筆部分 ここまで */} {items.map(item => { return <div key={item.id}> <input id={"input-" + item.id} type="text"></input> </div> })} <button onClick={onClickButton} type="button">追加</button> </> }