「Rorkで作ったアプリのTextInputに文字を入力していると、たまに1文字消えたり、カーソルが勝手に文末に飛んだりする」— この症状、開発者の手元では再現しにくいくせに、本番のユーザーレビューには確実に響く厄介な現象です。私もRorkで最初の数本を作っていたころ、ストアのレビューで「文字入力で1文字飛ぶ」という指摘をもらって、ようやく自分のコードの問題に気づきました。
「動いているように見えるのに、なぜかユーザーが気持ち悪がる」状態から抜け出すための実践的なチェックリストとして使ってください。
なぜTextInputで文字が消えたりカーソルが飛んだりするのか
React NativeのTextInputは、入力された1文字ごとにonChangeTextが呼ばれ、useStateで値を保持し、次のレンダリングでvalueプロパティとして書き戻す、という流れで動いています。この一連の流れのどこかで遅延・ループ・上書きが発生すると、ユーザーから見て「打った文字が消える」「カーソルが文末にワープする」といった症状になります。
特に厄介なのは、英語入力ではほとんど顕在化しないのに、日本語IMEや予測変換が走る環境でだけ症状が出るケースです。Rorkは英語圏のユーザーを想定したコードを生成することが多いため、生成直後のコードを何も考えずに本番投入すると、日本語ユーザーから不具合報告を受ける確率が高くなります。
これから紹介する3パターンは、いずれも「入力 → state更新 → 再レンダリング → value書き戻し」のサイクルが何らかの理由で乱れることが共通の原因です。順に見ていきましょう。
パターン1: 派生state を別変数で持って毎回再レンダリングしている
最も多いのは、TextInputを含む画面が、別のstateの更新によって毎キーストロークで余計に再レンダリングされているパターンです。
// ❌ 悪い例 — setText と setCount で再レンダリングが2回走り、
// IMEの未確定状態を巻き込んで1文字消えることがある
function NoteEditor() {
const [text, setText] = useState('');
const [count, setCount] = useState(0);
return (
<View>
<Text>文字数: {count} / 200</Text>
<TextInput
value={text}
onChangeText={(t) => {
setText(t);
setCount(t.length); // ← この2回目の更新が問題
}}
multiline
/>
</View>
);
}このコードの問題は、setTextとsetCountを続けて呼んでいる点です。Reactは通常これらをバッチ処理するのですが、IMEの未確定文字を挟んだ状態だと、まれに2回目の更新が「直前の入力をなかったこと」にしてしまいます。Rorkに「文字数カウンター付きのテキストエリアを作って」と頼むと、このパターンを書いてくることが少なくありません。
// ✅ 正しい書き方 — stateは1つに集約、派生値はレンダリング時に計算する
function NoteEditor() {
const [text, setText] = useState('');
return (
<View>
<Text>文字数: {text.length} / 200</Text>
<TextInput
value={text}
onChangeText={setText}
multiline
/>
</View>
);
}派生値(文字数・残り文字数・バリデーション結果など)は別のstateで持たず、レンダリング時に直接計算するか、必要ならuseMemoで導出するのが鉄則です。「state は信号、派生値は配線」と覚えておくと、無駄な再レンダリングを避けられます。
パターン2: 親から渡される value が遅れて入力を上書きする
2つ目は、TextInputが親コンポーネントからvalueを受け取っていて、その親側の更新が遅れてキー入力を上書きしてしまうパターンです。
// ❌ 悪い例 — onChangeText 内でAPI保存を await している
function ProfileForm({ initialName, onSave }: Props) {
const [name, setName] = useState(initialName);
const handleChange = async (t: string) => {
setName(t);
await onSave(t); // ← API保存の完了後に古い値が書き戻される
};
return <TextInput value={name} onChangeText={handleChange} />;
}ユーザーが「あいうえお」と入力している最中に、サーバー保存の完了で「あいう」がvalueに書き戻されると、その後に打った「えお」が消えたように見えます。日本語IMEの確定タイミングと重なると、未確定文字ごと消失することもあります。
// ✅ 正しい書き方 — 入力反映と保存処理を分離し、500ms のデバウンスをかける
function ProfileForm({ initialName, onSave }: Props) {
const [name, setName] = useState(initialName);
useEffect(() => {
const id = setTimeout(() => {
if (name !== initialName) onSave(name);
}, 500);
return () => clearTimeout(id);
}, [name]);
return <TextInput value={name} onChangeText={setName} />;
}入力UIの責務は「入力を受け付けてローカルstateに反映する」ことだけにし、保存処理は別レイヤーに切り出す — これは制御コンポーネント全般に通じる設計原則です。
パターン3: onChangeText 内でテキストを変換してループを作る
3つ目はパターン1・2に比べて遭遇率は低いものの、原因がわかりにくく、踏むと半日溶けます。onChangeText内で渡されたテキストを変換してからsetTextするパターンです。
// ❌ 悪い例 — 入力のたびに大文字変換、IMEの未確定文字も大文字化される
<TextInput
value={text}
onChangeText={(t) => setText(t.toUpperCase())}
/>英語入力では問題なく動くのですが、日本語IMEで「あ」を打って変換中の状態に対してtoUpperCase()を毎キーストローク適用すると、IMEバッファが壊れて変換候補が消える、未確定文字がローマ字のまま確定する、といった症状になります。先頭を自動で大文字化するUIや、半角カナを全角に強制変換するUIで特に発生しやすい落とし穴です。
// ✅ 正しい書き方 — 変換は確定後(onEndEditing or onBlur)に行う
<TextInput
value={text}
onChangeText={setText}
onEndEditing={(e) => setText(e.nativeEvent.text.toUpperCase())}
/>入力中のテキストには手を加えず、IMEが文字を確定したタイミング(onEndEditing、onBlur、または送信ボタンのハンドラ内)で変換処理を入れるのが鉄則です。「入力中のリアルタイム整形は基本的に悪手」と覚えておくと、似たバグを未然に防げます。
デバッグの順序 — まず何を疑うべきか
実際に手元のRorkアプリでTextInputの入力不具合に遭遇したら、私はいつもこの順序で切り分けます。
- まずTextInputを含む画面の関数の冒頭に
console.log('render');を入れて、1キー入力で何回再レンダリングされるかを数える。3回以上ならパターン1の疑いが濃厚です - 次に親から
valueが渡されている場合、その親側の更新ロジックに非同期処理(await、useEffect内のfetchなど)がないかを確認します。あればパターン2を疑います - それでも直らなければ、
onChangeTextの中身を一時的にsetText(t)だけに減らし、症状が消えるかを確認します。消えたらパターン3です
再現環境は「iOSの実機 × 標準IME × 日本語キーボード」で固定するのが最短ルートです。シミュレーターの英語キーボードでは多くの場合再現せず、Androidエミュレーターも入力レイテンシの違いで挙動が変わるため、実機テストの工程を省略しないことをおすすめします。
締めくくり
TextInputの入力不具合は、開発者の手元では再現しにくいくせに、ユーザーレビューには確実に響く領域です。Rorkは多くの場合うまく書いてくれますが、stateの分割や非同期処理を後から追加していくうちに、3つのパターンのいずれかに陥ることがあります。
直す順番は決まっていて、再レンダリング → 親stateの遅延 → onChangeText内の変換、の3つを上から順に疑うだけ。1つずつ可能性を消していけば、ほぼ確実に発生源にたどり着けます。次にRorkでアプリを作るときは、TextInputの実装が終わった段階で一度だけ日本語の実機テストを挟んでみてください。リリース後にレビューで気づくよりも、はるかに少ないコストで品質が上がるはずです。同じ系統の問題として、IME変換中のイベント暴発に手を焼いている方は、Rorkアプリの日本語入力が「変換中に暴発する」問題も併せて読むと、引き出しが増えると思います。