React入門 - コンポーネント、JSX、Props、Stateの基本

目次

はじめに

React を学び始めたときに、最初に戸惑うのが「なぜこんな複雑なのか」という感覚です。jQuery で育ったなら、DOM を直接操作する命令的アプローチが自然に思えるでしょう。しかし、React は全く異なる思想です。

初学者にとって、Props と State の違いは特にわかりにくい概念です。「どちらを使うべき?」「なぜ Props は変更できないの?」こうした疑問は、誰もが通る道です。

本記事では、React の最も基本的な概念である コンポーネント、JSX、Props、State を、初心者の視点に立って解説します。これらの4つの概念を深く理解することが、React 開発の土台となり、その後の Hook やデータ管理ライブラリも自然に理解できるようになります。

実際のコード例と、著者が初学者時代に戸惑った「ハマりポイント」を含めて、学習の効率化を図りました。

Reactとは

ReactはFacebookが開発したJavaScriptライブラリで、ユーザーインターフェース(UI)を効率的に構築するためのツールです。最大の特徴は、コンポーネント指向の開発と、宣言的UIです。

宣言的UIとは

従来のJavaScript(jQuery など)では、状態が変わった時に「どのようにDOM を操作するか」を命令的に書きます。

// 命令的な書き方(jQuery)
const count = 0;
document.getElementById("count").textContent = count;
 
button.addEventListener("click", () => {
  count++;
  document.getElementById("count").textContent = count; // 手動で DOM を更新
});

一方、Reactでは「現在の状態に対して、UIがどのように見えるべきか」を宣言します。状態が変わると、Reactが自動的にUIを更新します。

// 宣言的な書き方(React)
function Counter() {
  const [count, setCount] = useState(0);
 
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

この違いは、Reactを学ぶ上で最も重要なマインドシフトです。

JSX とは

JSXは、JavaScriptの中にHTMLのような構文を埋め込める拡張言語です。正式名称は「JavaScript XML」です。

JSX の基本

JSXで書いた以下のコード:

const element = <h1>Hello React!</h1>;

は、実際には以下のJavaScriptに変換されます:

const element = React.createElement("h1", null, "Hello React!");

つまり、JSXは単なる糖衣構文(シンタックスシュガー)です。ReactのcreateElement関数の呼び出しを、より読みやすく書くためのものです。

JSX の利点

  1. 可読性が高い - HTML に近い記述で直感的に理解できる
  2. バリデーション - TypeScriptやESLintで構文チェックが可能
  3. プログラマブル - JavaScriptの全機能が使える
function WelcomeBanner({ userName, isAdmin }) {
  return (
    <div>
      <h1>Welcome {userName}</h1>
      {isAdmin && <p>You have admin privileges</p>}
      <p>Today is {new Date().toLocaleDateString()}</p>
    </div>
  );
}

JSX の約束事

JSXを書く際には、いくつかのルールがあります。

1. 単一のルート要素を返す

// ❌ エラー: 複数の要素
function Bad() {
  return (
    <h1>Title</h1>
    <p>Description</p>
  );
}
 
// ✅ OK: ラッパー要素で囲む
function Good() {
  return (
    <div>
      <h1>Title</h1>
      <p>Description</p>
    </div>
  );
}
 
// ✅ OK: Fragment を使う
function Good2() {
  return (
    <>
      <h1>Title</h1>
      <p>Description</p>
    </>
  );
}

2. className を使う(class ではなく)

// ❌ class は JavaScript の予約語なので使えない
<div class="container">...</div>
 
// ✅ className を使う
<div className="container">...</div>

3. 属性名はキャメルケース

// ❌ ハイフンは使わない
<input on-change={handleChange} />
 
// ✅ キャメルケースで書く
<input onChange={handleChange} />

4. イベント名もキャメルケース

// ❌ 間違い
<button onclick={handleClick}>Click</button>
 
// ✅ 正しい
<button onClick={handleClick}>Click</button>

コンポーネント

Reactの最小単位はコンポーネントです。UIを小さな再利用可能なパーツに分割して、組み合わせることでアプリケーションを構築します。

関数コンポーネント

React v16.8 以降、関数コンポーネントが標準になりました。単純にJavaScript関数を定義するだけです。

function Welcome() {
  return <h1>Hello React!</h1>;
}
 
// 使用方法
function App() {
  return <Welcome />;
}

重要なルール: コンポーネント名は大文字で始める

// ✅ OK - 大文字で始まる
function MyComponent() {
  return <div>...</div>;
}
 
// ❌ NG - 小文字で始まるのはHTML要素と見なされる
function myComponent() {
  return <div>...</div>;
}

コンポーネントの作成パターン

// パターン1: シンプルな関数
function SimpleButton() {
  return <button>Click me</button>;
}
 
// パターン2: JSX を return する関数
function ProductCard() {
  return (
    <div className="card">
      <h2>Product Name</h2>
      <p>Price: $99</p>
    </div>
  );
}
 
// パターン3: 複数行の処理後に JSX を return
function UserGreeting() {
  const currentHour = new Date().getHours();
  const greeting = currentHour < 12 ? "Good morning" : "Good afternoon";
 
  return <h1>{greeting}</h1>;
}

Props(プロップス)

Props は、親コンポーネントが子コンポーネントに渡すデータです。コンポーネント間の通信の基本となります。

Props の基本

// 子コンポーネント
function Greeting(props) {
  return <h1>Hello, {props.name}!</h1>;
}
 
// 親コンポーネント
function App() {
  return <Greeting name="Alice" />;
}

分割代入を使った書き方

Props を分割代入することで、コードがより簡潔になります。

// Before: props.name, props.age, ...
function UserCard(props) {
  return (
    <div>
      <h2>{props.name}</h2>
      <p>Age: {props.age}</p>
      <p>Email: {props.email}</p>
    </div>
  );
}
 
// After: 分割代入を使う
function UserCard({ name, age, email }) {
  return (
    <div>
      <h2>{name}</h2>
      <p>Age: {age}</p>
      <p>Email: {email}</p>
    </div>
  );
}
 
// 使用例
function App() {
  return <UserCard name="Bob" age={30} email="bob@example.com" />;
}

Props の型定義(TypeScript で安全に)

TypeScript を使うと、Props の型を明示的に定義でき、実装時のミスを防ぐことができます。

// ステップ1: Props の型を interface で定義
interface UserCardProps {
  name: string;      // 必須の文字列
  age: number;       // 必須の数値
  email?: string;    // オプショナル(?で省略可能)
  isActive?: boolean; // オプショナル
}
 
// ステップ2: コンポーネントで型を指定
function UserCard({ name, age, email = 'unknown', isActive = true }: UserCardProps) {
  return (
    <div>
      <h2>{name}</h2>
      <p>Age: {age}</p>
      <p>Email: {email}</p>
      <p>{isActive ? 'Active' : 'Inactive'}</p>
    </div>
  );
}
 
// 使用例
<UserCard name="Alice" age={30} />              // ✅ OK(email, isActive は省略)
<UserCard name="Bob" age={25} email="bob@..." /> // ✅ OK
// <UserCard name="Charlie" /> // ❌ エラー:age が必須

型定義のポイント:

  • ? をプロパティ名の後に付けるとオプショナルになる
  • デフォルト値は = で指定できる
  • 間違った型を渡すと IDE で即座に警告される

Props として関数を渡す

コンポーネント間の通信で、親が子に関数を渡すことも一般的です。

interface ButtonProps {
  label: string;
  onClick: () => void;
}
 
function CustomButton({ label, onClick }: ButtonProps) {
  return <button onClick={onClick}>{label}</button>;
}
 
function App() {
  const handleClick = () => {
    alert("Button clicked!");
  };
 
  return <CustomButton label="Click me" onClick={handleClick} />;
}

Props は読み取り専用

重要な原則: Props は読み取り専用で、変更してはいけません。 Props に基づいて値を計算する場合は、新しい変数を作成します。

// ✅ 正しい: Props から新しい値を計算
function UserProfile({ name, age }: { name: string; age: number }) {
  const displayName = name.toUpperCase(); // Props を読むだけ
  const isAdult = age >= 18; // Props から計算
 
  return (
    <div>
      <h1>{displayName}</h1>
      <p>{isAdult ? "Adult" : "Minor"}</p>
    </div>
  );
}

State(ステート)

State は、コンポーネント内で管理される、時間とともに変わるデータです。Props と異なり、State は コンポーネント自身が変更できます

useState Hook の基本

import { useState } from "react";
 
function Counter() {
  const [count, setCount] = useState(0);
 
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

useState は以下のように機能します:

  1. 初期値: useState(0) で初期値を0に設定
  2. 戻り値: [現在の値, 更新関数] のペアを返す
  3. 状態の更新: setCount(新しい値) で状態を更新
  4. 再レンダリング: 状態が更新されると、コンポーネントが再レンダリングされる

State の実践例

import { useState } from "react";
 
function LoginForm() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [isLoading, setIsLoading] = useState(false);
 
  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setIsLoading(true);
 
    // ログイン処理
    try {
      const response = await fetch("/api/login", {
        method: "POST",
        body: JSON.stringify({ email, password }),
      });
 
      if (response.ok) {
        alert("Login successful!");
      }
    } catch (error) {
      alert("Login failed!");
    } finally {
      setIsLoading(false);
    }
  };
 
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="Password"
      />
      <button type="submit" disabled={isLoading}>
        {isLoading ? "Logging in..." : "Login"}
      </button>
    </form>
  );
}

Props と State の違い

特性PropsState
誰が所有するか親コンポーネントそのコンポーネント自身
変更可能かNo(不変)Yes(変更可能)
用途親から子へのデータ受け渡しコンポーネント内のデータ管理
初期値の設定親が設定useState で設定
変更時の動作変更しないことsetState で変更し、再レンダリング

State 更新の落とし穴

1. 前の状態に基づいて更新する場合は関数を使う

// ❌ 間違い: 複数回クリックすると期待と異なる結果になることがある
function Counter() {
  const [count, setCount] = useState(0);
 
  const handleClick = () => {
    setCount(count + 1);
    setCount(count + 1); // これは効果がない
  };
 
  return <button onClick={handleClick}>Count: {count}</button>;
}
 
// ✅ 正しい: 関数形式を使う
function Counter() {
  const [count, setCount] = useState(0);
 
  const handleClick = () => {
    setCount((prev) => prev + 1);
    setCount((prev) => prev + 1); // これは期待通りに動く
  };
 
  return <button onClick={handleClick}>Count: {count}</button>;
}

2. State はオブジェクトを含むことができるが、直接変更しない

// ❌ 間違い: オブジェクトを直接変更
function User() {
  const [user, setUser] = useState({ name: "Alice", age: 25 });
 
  const handleAgeIncrease = () => {
    user.age = 26; // ❌ 直接変更
    setUser(user); // React が変更を検出できない
  };
}
 
// ✅ 正しい: 新しいオブジェクトを作成
function User() {
  const [user, setUser] = useState({ name: "Alice", age: 25 });
 
  const handleAgeIncrease = () => {
    setUser({ ...user, age: user.age + 1 }); // スプレッド演算子で新しいオブジェクト
  };
}

Props と State の流れ(重要な概念)

React の最も重要なパターンは、**データが上から下に流れる(単一方向フロー)**ということです。

┌─────────────────────────────────────────┐
│           App(State を管理)             │
│          const [message, setMessage]    │
└──────────────┬──────────────────────────┘
               │ Props として message を渡す
               ↓
┌──────────────────────────────────────┐
│         Parent(Props を受け取る)      │
│        function Parent({ message })   │
└──────────────┬───────────────────────┘
               │ さらに Props として子に渡す
               ↓
┌──────────────────────────────────────┐
│        Child(最終的に表示)            │
│       <h1>{message}</h1>             │
└──────────────────────────────────────┘

このように、データは常に親から子へと流れます。逆向きは不可です。

function App() {
  const [message, setMessage] = useState("Hello");
 
  return (
    <div>
      {/* Props として子に渡す */}
      <Parent message={message} />
      <button onClick={() => setMessage("Hi")}>Change Message</button>
    </div>
  );
}
 
function Parent({ message }: { message: string }) {
  return (
    <div>
      {/* さらに孫に渡す */}
      <Child message={message} />
    </div>
  );
}
 
function Child({ message }: { message: string }) {
  return <h1>{message}</h1>;
}

このパターンでは:

  1. Appmessage という State を持つ
  2. Appmessage を Props として Parent に渡す
  3. Parentmessage を Props として Child に渡す
  4. Child は Props として message を受け取り表示する

データは常に上から下へ流れます。子から親への直接的な変更はできません。

実践例:Todo アプリ

ここまでの概念を組み合わせた、簡単な Todo アプリの例です。このアプリは以下を実装しています:

  • 複数の State 管理 - todos 配列と入力フィールドの状態
  • 子コンポーネントへの Props 渡し - todo データとコールバック関数
  • イベントハンドリング - クリック、入力、キープレスイベント
  • リスト操作 - map、filter などを使った配列操作
import { useState } from "react";
 
interface Todo {
  id: number;
  text: string;
  completed: boolean;
}
 
function TodoApp() {
  const [todos, setTodos] = useState<Todo[]>([]);
  const [input, setInput] = useState("");
 
  const addTodo = () => {
    if (input.trim() === "") return;
 
    const newTodo: Todo = {
      id: Date.now(),
      text: input,
      completed: false,
    };
 
    setTodos([...todos, newTodo]);
    setInput("");
  };
 
  const toggleTodo = (id: number) => {
    setTodos(
      todos.map((todo) =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo,
      ),
    );
  };
 
  const deleteTodo = (id: number) => {
    setTodos(todos.filter((todo) => todo.id !== id));
  };
 
  return (
    <div>
      <h1>My Todos</h1>
 
      <div>
        <input
          type="text"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyPress={(e) => e.key === "Enter" && addTodo()}
          placeholder="Add a new todo..."
        />
        <button onClick={addTodo}>Add</button>
      </div>
 
      <ul>
        {todos.map((todo) => (
          <TodoItem
            key={todo.id}
            todo={todo}
            onToggle={toggleTodo}
            onDelete={deleteTodo}
          />
        ))}
      </ul>
    </div>
  );
}
 
interface TodoItemProps {
  todo: Todo;
  onToggle: (id: number) => void;
  onDelete: (id: number) => void;
}
 
function TodoItem({ todo, onToggle, onDelete }: TodoItemProps) {
  return (
    <li
      style={{
        textDecoration: todo.completed ? "line-through" : "none",
      }}
    >
      <input
        type="checkbox"
        checked={todo.completed}
        onChange={() => onToggle(todo.id)}
      />
      <span>{todo.text}</span>
      <button onClick={() => onDelete(todo.id)}>Delete</button>
    </li>
  );
}

このアプリでは以下の概念が使われています:

  • JSX: UI を宣言的に記述
  • コンポーネント: TodoAppTodoItem に分割
  • Props: TodoItemtodo とコールバック関数を渡す
  • State: todosinput の状態管理
  • イベントハンドリング: onClick, onChange, onKeyPress

よくあるエラーと解決策

React 初心者が最初に出くわしやすいエラーと、その原因・解決法を紹介します。

エラー1: "Objects are not valid as a React child"

症状:

function Bad() {
  const user = { name: "Alice" };
  return <div>{user}</div>; // ❌ エラー
}

原因: オブジェクト全体を JSX に直接埋め込もうとしている

解決策:

function Good() {
  const user = { name: "Alice" };
  return <div>{user.name}</div>; // ✅ オブジェクトのプロパティを指定
}

エラー2: "Cannot update a component while rendering a different component"

症状: State 更新が無限ループして警告が出る

原因: コンポーネント内で直接 setState を呼んでいる(useEffect の外で)

解決策:

import { useState, useEffect } from "react";
 
function Good() {
  const [count, setCount] = useState(0);
 
  useEffect(() => {
    // ここで State を更新する
    setCount(1);
  }, []); // 空の依存配列で、マウント時に1度だけ実行
 
  return <div>{count}</div>;
}

エラー3: "Each child in a list should have a unique 'key' prop"

症状: リストをレンダリングすると警告が出る

原因: リスト要素に key props を指定していない

解決策:

// ❌ 間違い: インデックスを key にする(リストが並び替わると問題になる)
{
  todos.map((todo, index) => <TodoItem key={index} todo={todo} />);
}
 
// ✅ 正しい: 一意の ID を key にする
{
  todos.map((todo) => <TodoItem key={todo.id} todo={todo} />);
}

エラー4: "Cannot read property 'X' of undefined"

症状: Props を受け取るコンポーネントで、Props が undefined

原因: Props の型チェックをしていない、または初期値を設定していない

解決策:

// TypeScript で型定義
interface UserProps {
  name?: string; // オプショナルプロパティ
}
 
function User({ name = "Guest" }: UserProps) {
  return <div>{name}</div>;
}

よくある質問

Q1: Props と State はどう使い分けるの?

簡単な法則:

  • データが変わる可能性があるか? → State を使う
  • 親コンポーネントで管理されているか? → Props で受け取る

例えば、フォームの入力値は時間とともに変わるので State です。一方、ユーザー情報は親が管理していて、子は受け取るだけなので Props です。

Q2: State を頻繁に更新すると遅くなる?

React は効率的に設計されており、State の更新はバッチ処理されます。通常のアプリケーションでは、State の更新頻度によるパフォーマンス問題はほぼ発生しません。

ただし、非常に大きなリスト(1000 個以上の要素)を扱う場合は、仮想化(virtualization)を検討します。

Q3: 子コンポーネントから親の State を変更できる?

直接は不可です。代わりに:

// 親が子に関数を Props として渡す
function Parent() {
  const [count, setCount] = useState(0);
 
  return <Child onIncrement={() => setCount(count + 1)} />;
}
 
function Child({ onIncrement }) {
  return <button onClick={onIncrement}>Increment</button>;
}

このパターンで、子が親の State を間接的に変更できます。

Q4: JSX で JavaScript のロジックを書けるの?

はい、{} の中に書けます:

function Example({ items }) {
  return (
    <div>
      {items.filter((item) => item.active).length > 0 ? (
        <p>Active items: {items.filter((item) => item.active).length}</p>
      ) : (
        <p>No active items</p>
      )}
    </div>
  );
}

ただし、複雑なロジックはコンポーネントの外に関数として抽出する方が良いです。

Q5: 複数の State を持つことはできる?

**もちろんです。**1 つのコンポーネントで複数の useState を使用できます:

function Form() {
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [submitted, setSubmitted] = useState(false);
 
  // 複数の State を管理
}

デバッグのコツ

方法1: console.log を活用

Props や State の値を確認する最も基本的な方法です:

function Component({ name }: { name: string }) {
  const [count, setCount] = useState(0);
 
  console.log("Props:", name);
  console.log("State:", count);
 
  return (
    <div>
      {name} - {count}
    </div>
  );
}

ポイント: レンダリングが何度も実行される可能性があるため、console.log が複数回出力されることは正常です。

方法2: React DevTools を使う

ブラウザに React DevTools 拡張機能をインストールすると、Props と State をビジュアルで確認できます:

  1. ブラウザの DevTools で「Components」タブを開く
  2. 確認したいコンポーネントをクリック
  3. 右パネルで Props と State をリアルタイム確認
  4. State を変更してテストすることも可能

方法3: ESLint で早期発見

eslint-plugin-react-hooks を使うと、一般的な React の問題を事前に検出できます:

{
  "extends": ["plugin:react-hooks/recommended"]
}

これで、以下のような問題が自動で検出されます:

  • Hook の使い方の誤り
  • 依存配列の不足
  • Props の誤用

React 開発での個人的なハマりポイント

React を学ぶ上で、特に注意が必要な点を3つ紹介します:

ハマりポイント1: State は即座には更新されない

State の更新は非同期です。つまり、setState の直後に State を使用しても、古い値が取得されます:

function Counter() {
  const [count, setCount] = useState(0);
 
  const handleClick = () => {
    setCount(count + 1);
    console.log(count); // まだ古い値が出力される(count + 1 ではない)
  };
 
  return <button onClick={handleClick}>Count: {count}</button>;
}

対策: State の値が更新されたことをテストするには useEffect を使う:

useEffect(() => {
  console.log("Count updated:", count);
}, [count]);

ハマりポイント2: Props は親からの「指示」(子から変更不可)

子が Props を変更することはできません。よくある誤解をまとめます:

アンチパターン1: Props を直接変更

// ❌ 間違い
function UserCard(props) {
  props.name = "Bob"; // これは無視されるか、エラーになる
  return <h1>{props.name}</h1>;
}

アンチパターン2: Props を直接 State に代入

// ❌ 間違い:親が Props を更新してもローカル State は更新されない
function Counter({ initialCount }) {
  const [count, setCount] = useState(initialCount); // initialCount の値で固定
  return <div>{count}</div>;
}

正解: 親から関数を受け取る

// ✅ 正しい:親が updateName 関数を子に渡す
function Parent() {
  const [name, setName] = useState("Alice");
 
  return <Child name={name} onNameChange={setName} />;
}
 
function Child({ name, onNameChange }) {
  return (
    <button onClick={() => onNameChange("Bob")}>Change name: {name}</button>
  );
}

このパターンで、子は親の State を間接的に操作できます。

ハマりポイント3: リスト要素に配列のインデックスを key に使わない

インデックスを key にすると、リストが並び替わった時に予期しない動作が発生します:

// ❌ このような書き方は避ける
{
  items.map((item, index) => <Item key={index} value={item} />);
}
 
// ✅ 各要素に一意な ID がある場合、それを key に
{
  items.map((item) => <Item key={item.id} value={item} />);
}

学習ロードマップ

React の基本を習得した後、次に学ぶべき概念があります:

  1. 複数の State 管理ライブラリ - Redux、Zustand、Jotai など
  2. 副作用の管理 - useEffect、useCallback などの Hook
  3. パフォーマンス最適化 - useMemo、React.memo の活用
  4. フォーム管理 - React Hook Form などのライブラリ
  5. テスト - Jest、React Testing Library での単体テスト

まずは Props と State の流れをしっかり理解することが、後の学習をスムーズにします。

ベストプラクティス

React 開発を進める上で心がけるべき重要なポイント:

  1. コンポーネントの責任は単一にする - 1つのコンポーネントは1つのことをよくやるべき
  2. Props は親から子へ(単一方向) - 逆向きのデータフローを避ける
  3. State は必要最小限に - どのコンポーネントが State を持つべきかを慎重に考える
  4. 再利用性を意識する - 他の場所でも使えるコンポーネント設計を心がける
  5. デバッグ時は React DevTools を活用 - Props と State の変化を追跡できます

まとめ

本記事では、React の基本概念である JSX、コンポーネント、Props、State について深く学びました。これらは React の全機能の基礎となり、後で学ぶ Hook やデータ管理も、これらの概念の上に構築されています。

学んだ概念の関係性:

JSX(HTMLのような構文)
  ↓
コンポーネント(JSX を返す関数)
  ↓
Props(親から子へ)& State(コンポーネント内)
  ↓
単一方向フロー(データは常に上から下へ)

重要な3つの原則:

  1. Props は読み取り専用 - 子は親から受け取った Props を変更できない
  2. State は必要な場所で - State を持つべきコンポーネントを意識的に選ぶ
  3. データは上から下へ - 逆向きの通信はコールバック関数で実現

最初は複雑に感じるかもしれませんが、実際にコードを書きながら試す中で、これらの概念が自然と身につきます。「エラーが出た → よくあるエラーセクションを見る → 修正する」 このループを何度も経験することが、最も効果的な学習方法です。


実装してみよう

本記事で学んだ概念を実装してみるのが最も効果的です。以下のステップで進めてください:

  1. Create React App で新規プロジェクト作成

    npx create-react-app my-react-app
    cd my-react-app
    npm start
  2. シンプルなコンポーネントから始める

    • ボタンをクリックするとカウンターが増えるコンポーネント
    • Props で名前を受け取り、挨拶を表示するコンポーネント
  3. デバッグを体験する

    • React DevTools を開いて、State の変化を追跡
    • console.log で Props を確認
  4. Todo アプリを実装する

    • 本記事で紹介した Todo アプリを自分で実装
    • 動作確認、デバッグを通じて学習を深化

学習の鍵は「動かして、壊して、直す」です。エラーが出たら、本セクションの「よくあるエラーと解決策」を参照してください。

関連記事