コンテンツにスキップ
Webフロントエンドエンジニアのための React Native 実践入門
Esc
navigateopen⌘Jpreview
このページの内容

第8章 React Nativeのユーザー入力

タップにPressableを使う理由から入力欄がキーボードに隠れる問題への対処まで、モバイルの2大操作をひととおり実装できます。

モバイルアプリでのユーザー入力は、「タップ」と「テキスト入力」の2つが大半を占めます。本章では、それぞれをReact Nativeでどう扱うかを見ていきます。

タップ系コンポーネント

Webの<button>に相当するものとして、React Nativeにはタッチ可能なコンポーネントがいくつかあります。歴史的経緯で複数存在していますが、新規実装ではPressableを第一選択にしてください。

Pressable

最も柔軟で、推奨される選択肢です。

import { Pressable, Text } from "react-native";

<Pressable
  onPress={() => console.log("tapped")}
  style={({ pressed }) => ({
    opacity: pressed ? 0.6 : 1,
    backgroundColor: "#007aff",
    padding: 12,
    borderRadius: 8,
  })}
>
  <Text style={{ color: "#fff", textAlign: "center" }}>送信</Text>
</Pressable>

styleにオブジェクトではなく関数を渡すと、pressed(押下中かどうか)、hovered(マウスホバー時。Web対応用)、focused(フォーカス時)の状態に応じてスタイルを変えられます。

押下中の見た目を変えたい時は、ほぼpressedを見れば足ります。

Pressableのその他のイベント

onPress以外にも、

  • onPressIn ― 押し始めた瞬間
  • onPressOut ― 指を離した瞬間
  • onLongPress ― 長押し
  • delayLongPress ― 長押しと判定するまでの時間(ms)

など、状況に応じて使い分けられます。

Touchable系の現在地

過去にはTouchableOpacityTouchableHighlightTouchableWithoutFeedbackTouchableNativeFeedbackといったコンポーネントが使われていました。今でも動きますが、Pressableがこれらの機能を統合しているため、新規実装で選ぶ理由はほとんどありません。

既存のコードベースに残っている分はそのままでも構いませんが、新しい画面はPressableで揃えていくのがおすすめです。

ボタン代わりのラッパーコンポーネント

毎回<Pressable>にスタイルを書くのは冗長なので、共通のボタンコンポーネントを作っておくと便利です。

import { Pressable, Text } from "react-native";

type ButtonProps = {
  label: string;
  onPress: () => void;
  variant?: "primary" | "secondary";
};

export function Button({ label, onPress, variant = "primary" }: ButtonProps) {
  return (
    <Pressable
      onPress={onPress}
      style={({ pressed }) => ({
        opacity: pressed ? 0.6 : 1,
        backgroundColor: variant === "primary" ? "#007aff" : "#eee",
        padding: 12,
        borderRadius: 8,
      })}
    >
      <Text style={{ color: variant === "primary" ? "#fff" : "#333", textAlign: "center" }}>
        {label}
      </Text>
    </Pressable>
  );
}

イベントハンドラはpropsで受け取り、内部の関数名はhandleプレフィックスにする ― といった命名は、Web開発と同じ感覚で問題ありません。

ジェスチャーは別ライブラリで

「スワイプで削除」「ピンチで拡大」「ドラッグで並び替え」のような複雑なジェスチャーは、PressableではなくReact Native Gesture Handlerという別ライブラリで実装します。

第12章でアニメーションと一緒に扱うので、ここでは「タップ以上の操作はGesture Handlerに任せる」という方針だけ覚えておいてください。

キーボードの扱い

ここからがモバイル特有の話です。Web開発では「キーボードの存在」を意識する場面はほぼありませんが、モバイルでは入力欄をタップした瞬間に画面の下半分以上をキーボードが覆います。これに対する備えが必要です。

よくある問題

入力欄を画面下のほうに置いていると、キーボードが立ち上がった時に入力欄が隠れてしまいます。Webならscroll-into-viewが自動で効いたり、CSSで対応できたりしますが、React Nativeでは明示的に対処します。

キーボード対策なしでは入力欄が隠れ、KeyboardAvoidingViewでは入力欄をキーボードの上に保てる比較

標準のKeyboardAvoidingView

React Nativeに標準で入っている対応はKeyboardAvoidingViewです。キーボードの出現に応じて、自分の高さやpaddingを調整してくれます。

import { KeyboardAvoidingView, Platform, TextInput } from "react-native";

function Form() {
  return (
    <KeyboardAvoidingView
      style={{ flex: 1 }}
      behavior={Platform.OS === "ios" ? "padding" : "height"}
    >
      <TextInput placeholder="入力" />
    </KeyboardAvoidingView>
  );
}

behaviorプロパティに何を指定するかでiOSとAndroidの挙動が変わります。iOSはpadding、Androidはheightまたはundefinedが扱いやすい、というのが経験則です。

ただし、KeyboardAvoidingViewはあくまで標準提供の最小限の機能で、複雑な画面構成だと「うまくいかない」「位置がカクつく」ことが少なくありません。

react-native-keyboard-controller

より滑らかで安定したキーボード制御を求める場合は、サードパーティのreact-native-keyboard-controllerを導入するのが現代の定番です。

npx expo install react-native-keyboard-controller

このライブラリの強みは、

  • ネイティブ側でアニメーションを処理するため、滑らかでカクつかない
  • iOSとAndroidの挙動を揃えやすい
  • 入力欄の上部に「次へ/完了」のツールバーを簡単に出せる
  • キーボード出現時に特定要素を自動でスクロールしてフォーカスに合わせられる

といった点です。本格的にフォームを作るアプリでは、最初からreact-native-keyboard-controllerを入れてしまうのがおすすめです。

import { KeyboardAvoidingView } from "react-native-keyboard-controller";

function Form() {
  return (
    <KeyboardAvoidingView style={{ flex: 1 }} behavior="padding">
      <TextInput placeholder="入力" />
    </KeyboardAvoidingView>
  );
}

APIは標準のKeyboardAvoidingViewと似ているので、置き換えは比較的スムーズに行えます。

キーボードを閉じる

「画面の余白をタップしたらキーボードを閉じたい」という挙動は、Webでは自動でしたがモバイルでは明示的に閉じる必要があります。

import { Keyboard, Pressable } from "react-native";

<Pressable onPress={Keyboard.dismiss} style={{ flex: 1 }}>
  {/* 中身 */}
</Pressable>

Keyboard.dismiss()を呼べばキーボードが閉じます。タップ時の見た目を変えたくないので、ここではPressablepressedスタイルは指定していません。

画面全体をラップしてキーボードを閉じる用途では、伝統的にTouchableWithoutFeedbackが使われてきました。既存コードで見かけても驚かないでください。react-native-keyboard-controllerを使っている場合は、ライブラリ側にもキーボードを閉じる仕組みが用意されています。

フォームのバリデーション

フォームのバリデーションや状態管理は、Web側で使い慣れているライブラリ(React Hook Form、TanStack Form、Valibotなど)がそのまま使えます。React Nativeに固有の事情はほとんどありません。

import { useForm, Controller } from "react-hook-form";
import { TextInput, Text, View } from "react-native";

function NameForm() {
  const { control, handleSubmit, formState } = useForm({ defaultValues: { name: "" } });

  return (
    <View>
      <Controller
        control={control}
        name="name"
        rules={{ required: "名前は必須です" }}
        render={({ field: { value, onChange } }) => (
          <TextInput value={value} onChangeText={onChange} placeholder="名前" />
        )}
      />
      {formState.errors.name && <Text>{formState.errors.name.message}</Text>}
    </View>
  );
}

ポイントは、HTMLの<input>がそのまま動くわけではないので、Controllerコンポーネントを介して<TextInput>と接続することです。

本章のまとめ

  • タップ系はPressableを第一選択。Touchable系は既存コード互換のために残っている
  • 共通ボタンコンポーネントを作って繰り返し使うとコードがすっきりする
  • キーボード制御は標準のKeyboardAvoidingViewより、react-native-keyboard-controllerが現代の定番
  • フォームライブラリはWeb側のものがほぼそのまま使える(<TextInput>Controllerで包む)

次章では、画面と画面をつなぐ仕組み ― ナビゲーションを扱います。Expo Routerによるファイルベースルーティングが主役です。

最終更新 2026年7月24日