今日からReact Nativeを始めたい人のための速習React(前編)
この記事はReact Native 全部俺 Advent Calendar 5日目の記事です。
このアドベントカレンダーについて
このアドベントカレンダーは @itome が全て書いています。
基本的にReact NativeおよびExpoの公式ドキュメントとソースコードを参照しながら書いていきます。誤植や編集依頼はXにお願いします。
今日からReact Nativeを始めたい人のための速習React (前編)
モバイルアプリ開発のためにReact Nativeを始めようとしている方向けに、Reactの基礎を解説します。この記事では、Reactの基本的な概念とコンポーネントの書き方を中心に説明します。
Reactとは?
Reactは、FacebookによってWebアプリケーションのUIを構築するために開発されたJavaScriptライブラリです。最大の特徴は、UIを小さな部品(コンポーネント)に分割して開発できる点です。
例えば、以下のようなプロフィールカードのUIを作る場合:
type ProfileCardProps = {
name: string;
age: number;
bio: string;
};
const ProfileCard = ({ name, age, bio }: ProfileCardProps) => {
return (
<div className="card">
<h2>{name}</h2>
<p>年齢: {age}歳</p>
<p>{bio}</p>
</div>
);
};
<ProfileCard name="太郎" age={10} bio="説明文" />
このように、再利用可能なコンポーネントとして実装できます。
ReactとReact Nativeの関係
ReactとReact Nativeは、同じ考え方と原則に基づいています。主な違いは出力される結果です:
- React → HTML要素(
<div>
,<p>
など) - React Native → ネイティブUIコンポーネント(
<View>
,<Text>
など)
// Reactの場合
const WebButton = () => {
return <button>クリック</button>;
};
// React Nativeの場合
const NativeButton = () => {
return <Pressable><Text>クリック</Text></Pressable>;
};
つまり、Reactの知識はReact Nativeでも直接活用できます。
クラスコンポーネントと関数コンポーネント
Reactのコンポーネントには2つの書き方があります:
クラスコンポーネント(古い書き方)
class Welcome extends React.Component<{ name: string }> {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
関数コンポーネント(現在主流な書き方)
const Welcome = ({ name }: { name: string }) => {
return <h1>Hello, {name}</h1>;
};
クラスコンポーネントは非推奨というわけではありませんが、新しいReactの機能の多くは関数コンポーネントを前提としており、これから始める方は関数コンポーネントに集中して学習することをお勧めします。以降の説明も関数コンポーネントを前提として書いていきます。
関数コンポーネントの書き方
基本的な構文
type TodoItemProps = {
title: string;
completed: boolean;
};
const TodoItem = ({ title, completed }: TodoItemProps) => {
return (
<div>
<input type="checkbox" checked={completed} />
<span>{title}</span>
</div>
);
};
変数の埋め込み
JSX(ReactのHTML-likeな構文)内で変数や式を使用する場合は、中括弧 {}
で囲みます:
const Greeting = () => {
const user = "太郎";
const currentTime = new Date().getHours();
return (
<div>
<p>{user}さん、こんにちは!</p>
<p>現在の時刻は{currentTime}時です。</p>
</div>
);
};
Props(プロパティ)の受け取り方
コンポーネントは引数としてpropsを受け取ります:
type ButtonProps = {
label: string;
onClick: () => void;
disabled?: boolean; // オプショナルなprop
};
const Button = ({ label, onClick, disabled = false }: ButtonProps) => {
return (
<button onClick={onClick} disabled={disabled}>
{label}
</button>
);
};
条件分岐の書き方
Reactでは、条件分岐を以下のような方法で実装できます:
三項演算子
const UserStatus = ({ isOnline }: { isOnline: boolean }) => {
return (
<div>
{isOnline ? (
<span className="online">オンライン</span>
) : (
<span className="offline">オフライン</span>
)}
</div>
);
};
論理演算子(&&)
const Notification = ({ message }: { message?: string }) => {
return (
<div>
{message && <p className="notification">{message}</p>}
</div>
);
};
変数への代入
const UserProfile = ({ user }: { user?: User }) => {
let content: ReactNode;
if (!user) {
content = <p>ユーザーが見つかりません</p>;
} else if (user.isAdmin) {
content = <AdminView user={user} />;
} else {
content = <NormalUserView user={user} />;
}
return <div className="profile">{content}</div>;
};
リストの表示
配列の要素を表示する場合は、map
メソッドを使用します:
type Todo = {
id: string;
title: string;
completed: boolean;
};
const TodoList = ({ todos }: { todos: Todo[] }) => {
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}> {/* keyは必須! */}
<input type="checkbox" checked={todo.completed} />
<span>{todo.title}</span>
</li>
))}
</ul>
);
};
keyの重要性
map
でリストを生成する際は、必ず各要素に一意のkey
プロパティを指定する必要があります:
// 良い例
{items.map(item => <Item key={item.id} {...item} />)}
// 避けるべき例
{items.map((item, index) => <Item key={index} {...item} />)}
indexをkeyとして使用するのは、リストの要素が追加・削除される可能性がある場合は避けるべきです。
React Nativeでのリスト
React Nativeで長いリストを扱う場合は、FlatList
コンポーネントを使用することが推奨されます。.map
を使ったレンダリングに比べてビューポート(画面に表示されている範囲)の外に表示される部分の処理をスキップできるため、パフォーマンスに優れています。
const TodoList = ({ todos }: { todos: Todo[] }) => {
return (
<FlatList
data={todos}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<TodoItem title={item.title} completed={item.completed} />
)}
/>
);
};
コンポーネントの純粋性
Reactの関数コンポーネントは、UIが変更されるたびに何度でも繰り返し呼ばれます。そのため可能な限り純粋な関数として実装すべきです。つまり
- 同じpropsに対して常に同じ結果を返す
- 副作用(APIコール、タイマー、データの変更など)は避ける
// 良い例
const PriceDisplay = ({ price }: { price: number }) => {
return <p>{price.toLocaleString()}円</p>;
};
// 避けるべき例
const BadPriceDisplay = ({ price }: { price: number }) => {
// コンポーネント内で直接副作用を起こすのは避ける
fetch('/api/log-price', { method: 'POST', body: String(price) });
return <p>{price.toLocaleString()}円</p>;
};
副作用が必要な場合は、後編で解説するReact Hooksを使用します。
まとめ
この前編では、Reactの基本的な概念とコンポーネントの書き方について解説しました。後編では、より高度な機能を実現するためのReact Hooksについて詳しく説明します。
Discussion