AI時代のReact入門 第3回:モダンJavaScript基礎①(アロー関数、let/const)

「React のコードを見ると、なんだか見慣れない書き方ばかり…」

HTML、CSS、jQueryでの開発経験がある方なら、このような感覚をお持ちではないでしょうか。

確かに、モダンなJavaScriptの文法は、従来の書き方とは大きく異なる部分があります。

JS
// jQueryでの書き方
$('.button').click(function() {
  var count = 0;
  count = count + 1;
});

// Reactでの書き方
const Button = () => {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>
    {count}
  </button>;
};

しかし、これらの新しい文法は、よりメンテナンスしやすく、バグの少ないコードを書くために導入された進化です。

今回は、V0でのプロトタイピングとCursorでの実装を通じて、特に重要な2つの機能—アロー関数とlet/constについて学んでいきましょう。

AIの力を借りることで、これらの新しい概念もスムーズに理解できるはずです。

はじめに

React/Next.jsでの開発において、モダンJavaScriptの理解は必要不可欠です。しかし、従来のJavaScript(特にjQuery)での開発に慣れた方にとって、新しい文法や概念の習得は大きなハードルとなることがあります。

なぜモダンJavaScriptを学ぶのか

実際のコードを見比べてみましょう:

JS
// jQueryでの一般的な実装
$(document).ready(function() {
  var counter = 0;
  $('#incrementButton').click(function() {
    counter = counter + 1;
    $(this).text(counter);
  });
});

// Next.jsでのモダンな実装
'use client';

const Counter = () => {
  const [count, setCount] = useState(0);
  
  return (
    <button onClick={() => setCount(count + 1)}>
      {count}
    </button>
  );
};

一見すると大きく異なるこれらのコードですが、実は同じことを実現しています。モダンな実装の方が:

  • コードが簡潔
  • 状態管理が明確
  • バグが発生しにくい
  • テストがしやすい

といった利点があります。

AIを活用した学習方法

このような新しい文法や概念を学ぶ際、V0とCursorを効果的に活用できます:

  1. V0での概念理解
    • 基本的な文法の説明を求める
    • コードの変換例を生成
    • 使用シーンの確認
  2. Cursorでの実践
    • 実際のコードを書く
    • エラーの解決
    • コードの最適化

このように、AIツールを「先生」として活用することで、モダンJavaScriptの学習をより効率的に進めることができます。

では、まず変数宣言の新しい方法から見ていきましょう。

変数宣言の進化

変数宣言は、JavaScriptの基本中の基本です。しかし、モダンJavaScriptでは、従来のvarに加えてletconstという2つの新しい宣言方法が導入されました。

AIツールを活用しながら、これらの違いと使い分けについて理解していきましょう。

V0でのプロトタイプ検証

まず、V0に基本的な使い方について質問してみましょう:

質問:
var、let、constの基本的な違いを、
具体的なコード例と共に説明してください。
また、Next.jsでの一般的な使用パターンも
教えてください。

V0は以下のような回答を提供するでしょう:

JS
// 1. スコープの違い
function scopeExample() {
  // varはブロックスコープを持たない
  if (true) {
    var x = 1;  // 関数スコープ
    let y = 2;  // ブロックスコープ
    const z = 3;  // ブロックスコープ
  }
  console.log(x);  // 1
  // console.log(y);  // エラー
  // console.log(z);  // エラー
}

// 2. 再代入と再宣言
var count = 1;
var count = 2;  // OK

let value = 1;
// let value = 2;  // エラー
value = 2;  // OK

const MAX = 1;
// MAX = 2;  // エラー

このコードを基に、実際の使い分けについて更に詳しく見ていきましょう。

Cursorでの実践的な実装

プロトタイプの理解を基に、Cursorで実践的なコードを書いていきましょう。特に、Next.jsでのコンポーネント実装において、変数宣言をどのように使い分けるかを見ていきます。

JS
'use client';

import { useState, useEffect } from 'react';

// 定数はconstで宣言
const MAX_RETRY_COUNT = 3;
const API_ENDPOINT = '/api/data';

interface DataItem {
  id: number;
  name: string;
}

const DataFetchComponent = () => {
  // コンポーネントの状態はconst + useStateで管理
  const [data, setData] = useState<DataItem[]>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);

  // 関数定義もconstで
  const fetchData = async () => {
    // ブロックスコープ内の変数はletを使用
    let retryCount = 0;
    setLoading(true);

    while (retryCount < MAX_RETRY_COUNT) {
      try {
        const response = await fetch(API_ENDPOINT);
        const jsonData = await response.json();
        setData(jsonData);
        break;
      } catch (err) {
        retryCount++;
        if (retryCount === MAX_RETRY_COUNT) {
          setError(err as Error);
        }
      }
    }

    setLoading(false);
  };

  useEffect(() => {
    fetchData();
  }, []);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      {data.map(item => (
        <div key={item.id}>{item.name}</div>
      ))}
    </div>
  );
};

export default DataFetchComponent;

このコードでの変数宣言の使い分けのポイントは:

  1. constを使う場合
    • コンポーネント自体の定義
    • 定数値の定義(MAX_RETRY_COUNT, API_ENDPOINT)
    • useState hookの戻り値
    • 関数の定義
    • mapのコールバック関数
  2. letを使う場合
    • ループカウンター
    • 途中で値が変更される変数
    • 一時的な計算結果の保存
  3. varは使わない
    • スコープの問題を避けるため
    • 予期せぬ巻き上げ(hoisting)を防ぐため

このような使い分けにより:

  • コードの意図が明確になる
  • バグの発生を防げる
  • コードの保守性が向上する

次のセクションでは、もう一つの重要な機能であるアロー関数について見ていきましょう。

アロー関数の基礎

アロー関数は、モダンJavaScriptで最も頻繁に使用される機能の一つです。特にReactやNext.jsの開発では、コンポーネントの定義やコールバック関数で必ず使用することになります。

V0でのプロトタイプ検証

まず、V0に基本的な使い方を質問してみましょう:

質問:
従来の関数とアロー関数の違いを、
具体的なコード例と共に説明してください。
特にReact/Next.jsでの使用例も含めて
教えてください。

V0は以下のような例を示してくれます:

JS
// 1. 基本的な構文の違い
// 従来の関数
function add(a, b) {
  return a + b;
}

// アロー関数
const add = (a, b) => {
  return a + b;
};

// 単純な場合は更に省略可能
const add = (a, b) => a + b;

// 2. thisの扱いの違い
// 従来の関数
const counter = {
  count: 0,
  increment: function() {
    setTimeout(function() {
      this.count++; // thisが期待通りに動作しない
    }, 1000);
  }
};

// アロー関数
const counter = {
  count: 0,
  increment: function() {
    setTimeout(() => {
      this.count++; // thisが正しく参照される
    }, 1000);
  }
};

この基本的な理解を元に、Next.jsでの実践的な使い方を見ていきましょう。

Cursorでの実践的な実装

実際のNext.jsコンポーネントで、アロー関数の様々な使用パターンを実装してみましょう:

TS
'use client';

import { useState, useEffect, useCallback } from 'react';

interface TodoItem {
  id: number;
  text: string;
  completed: boolean;
}

// コンポーネント自体をアロー関数で定義
const TodoList = () => {
  const [todos, setTodos] = useState<TodoItem[]>([]);
  const [newTodo, setNewTodo] = useState('');

  // イベントハンドラをアロー関数で定義
  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setNewTodo(e.target.value);
  };

  // useCallbackと組み合わせた使用例
  const handleAddTodo = useCallback(() => {
    if (!newTodo.trim()) return;

    setTodos(prevTodos => [
      ...prevTodos,
      {
        id: Date.now(),
        text: newTodo,
        completed: false
      }
    ]);
    setNewTodo('');
  }, [newTodo]);

  // 配列メソッドのコールバックとしての使用
  const incompleteTodos = todos.filter(todo => !todo.completed);
  
  // JSXでのインラインアロー関数
  return (
    <div className="p-4">
      <div className="flex gap-2 mb-4">
        <input
          type="text"
          value={newTodo}
          onChange={handleInputChange}
          className="border p-2 rounded"
        />
        <button
          onClick={handleAddTodo}
          className="bg-blue-500 text-white px-4 py-2 rounded"
        >
          Add
        </button>
      </div>
      
      <ul>
        {todos.map(todo => (
          <li 
            key={todo.id}
            onClick={() => {
              setTodos(prevTodos =>
                prevTodos.map(t =>
                  t.id === todo.id 
                    ? { ...t, completed: !t.completed }
                    : t
                )
              );
            }}
            className="cursor-pointer"
          >
            <span className={todo.completed ? 'line-through' : ''}>
              {todo.text}
            </span>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default TodoList;

このコードでのアロー関数の使用パターンを整理すると:

1. コンポーネントの定義

TS
const TodoList = () => { ... };

2. イベントハンドラ

TS
const handleInputChange = (e) => { ... };

3. useCallbackでのメモ化

TS
const handleAddTodo = useCallback(() => { ... }, []);

4. 配列メソッドのコールバック

TS
todos.filter(todo => !todo.completed);
todos.map(todo => ( ... ));

5. インラインのイベントハンドラ

TS
onClick={() => setTodos( ... )}

それぞれの使用パターンには明確な利点があります:

  • コードが簡潔になる
  • thisの問題を回避できる
  • 関数のスコープが明確になる

次のセクションでは、これらの知識を活用して、実際のjQueryコードをモダンな書き方に変換する練習をしていきましょう。

実践:jQueryのコードをモダンに書き換える

これまで学んだ変数宣言とアロー関数の知識を活用して、実際のjQueryコードをNext.jsのコードに書き換えていきましょう。具体的なユースケースを通じて、モダンな実装方法を体験していきます。

V0でのプロトタイプ作成

まず、よくあるjQueryのコードをV0に示して、Next.jsでの実装方法を聞いてみましょう:

質問:
以下のjQueryコードをNext.jsのコンポーネントに書き換えたいです:

$(document).ready(function() {
  var count = 0;
  $('#counter').text(count);
  
  $('#increment').click(function() {
    count++;
    $('#counter').text(count);
  });
  
  $('#reset').click(function() {
    count = 0;
    $('#counter').text(count);
  });
});

モダンな書き方で実装方法を提案してください。

V0は以下のようなプロトタイプコードを提案するでしょう:

TS
'use client';

import { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <div id="counter">{count}</div>
      <button id="increment">Increment</button>
      <button id="reset">Reset</button>
    </div>
  );
};

export default Counter;

このプロトタイプを基に、Cursorで更に改善していきましょう。

Cursorでの実装改善

質問:
このカウンターコンポーネントを以下の観点で改善したいです:
1. TypeScriptでの型定義
2. スタイリング(Tailwind CSS)
3. アニメーション効果
4. アクセシビリティ対応
具体的な実装例を示してください。
TS
'use client';

import { useState, useCallback } from 'react';
import { motion, AnimatePresence } from 'framer-motion';

interface CounterProps {
  initialValue?: number;
  onCountChange?: (count: number) => void;
}

const Counter: React.FC<CounterProps> = ({
  initialValue = 0,
  onCountChange,
}) => {
  const [count, setCount] = useState(initialValue);

  const handleIncrement = useCallback(() => {
    setCount(prev => {
      const newValue = prev + 1;
      onCountChange?.(newValue);
      return newValue;
    });
  }, [onCountChange]);

  const handleReset = useCallback(() => {
    setCount(initialValue);
    onCountChange?.(initialValue);
  }, [initialValue, onCountChange]);

  return (
    <div className="flex flex-col items-center gap-4">
      <AnimatePresence mode="wait">
        <motion.div
          key={count}
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          exit={{ opacity: 0, y: -20 }}
          className="text-4xl font-bold"
          aria-live="polite"
          aria-label={`Current count is ${count}`}
        >
          {count}
        </motion.div>
      </AnimatePresence>

      <div className="flex gap-2">
        <button
          onClick={handleIncrement}
          className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 
                   transition-colors duration-200 focus:outline-none focus:ring-2"
          aria-label="Increment counter"
        >
          Increment
        </button>

        <button
          onClick={handleReset}
          className="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600 
                   transition-colors duration-200 focus:outline-none focus:ring-2"
          aria-label="Reset counter"
        >
          Reset
        </button>
      </div>
    </div>
  );
};

export default Counter;

主な改善ポイント:

  1. 型の安全性
    • PropsにTypeScriptの型定義を追加
    • コールバック関数の型も定義
  2. パフォーマンス
    • useCallbackでイベントハンドラをメモ化
    • AnimatePresenceで不要なアニメーションを防止
  3. ユーザー体験
    • Tailwind CSSでスタイリング
    • ホバー・フォーカス状態の視覚的フィードバック
    • アニメーションによる数値の変更表示
  4. アクセシビリティ
    • 適切なaria属性の追加
    • フォーカス可視性の確保
    • スクリーンリーダー対応

このように、jQueryの命令的なコードから、Reactの宣言的なコードへと進化させることで、より保守性が高く、ユーザー体験の優れたコンポーネントが実現できます。

次のセクションでは、この実践を通じて学んだことをまとめていきましょう。

まとめ

今回は、モダンJavaScriptの基礎となる変数宣言(var/let/const)とアロー関数について、V0でのプロトタイピングとCursorでの実装を通じて学習しました。

学んだことの振り返り

  1. 変数宣言の進化
    • varからlet/constへの移行の意義
    • スコープの明確化
    • より安全な変数管理
  2. アロー関数の利点
    • シンプルな構文
    • thisの扱いの改善
    • コードの可読性向上
  3. jQueryからの移行のポイント
    • 命令的なコードから宣言的なコードへ
    • DOM操作からコンポーネント志向へ
    • 状態管理の明確化

実践を通じて、モダンな開発手法への理解を深めていきましょう。

それでは、次回もよろしくお願いします!