Reactの矢印関数とfunctionの違い|どっちを使うべき?

Reactコンポーネントでの矢印関数とfunction宣言の違いを詳しく解説。使い分けのポイントとパフォーマンス面での注意点を初心者向けに説明します。

Learning Next 運営
21 分で読めます

みなさん、Reactでコンポーネントを書く時、悩みませんか?

「矢印関数と普通の関数、どっちを使えばいいの?」 「何か違いがあるの?」 「パフォーマンスに影響する?」

こんな疑問を持ったことがありませんか?

実は、多くの初心者が同じように悩んでいます。 どちらも同じように動作しますが、実は細かな違いがあるんです。

この記事では、Reactでの矢印関数とfunction宣言の違いを初心者にもわかりやすく解説します。

最後まで読めば、適切な使い分けができるようになりますよ。

基本的な書き方の違い

まずは、2つの書き方を比べてみましょう。

見た目だけでも、結構違いがありますよね。

function宣言による書き方

普通の関数宣言は、こんな感じです。

// function宣言
function MyComponent() {
  return <div>Hello World</div>;
}

シンプルなコンポーネントですね。

もう少し複雑な例も見てみましょう。

// イベントハンドラーを含む例
function MyComponent() {
  const handleClick = () => {
    console.log('クリックされました');
  };
  
  return (
    <button onClick={handleClick}>
      クリック
    </button>
  );
}

この例では、コンポーネント自体はfunction宣言で書いています。 でも、イベントハンドラーのhandleClickは矢印関数で書いていますね。

矢印関数による書き方

矢印関数だと、こんな感じになります。

// 矢印関数
const MyComponent = () => {
  return <div>Hello World</div>;
};

functionキーワードの代わりに=>(矢印)を使います。

さらに省略もできます。

// 1行で書く省略形
const MyComponent = () => <div>Hello World</div>;

returnを省略して、よりコンパクトに書けますね。

イベントハンドラーを含む例も見てみましょう。

// イベントハンドラーを含む例
const MyComponent = () => {
  const handleClick = () => {
    console.log('クリックされました');
  };
  
  return (
    <button onClick={handleClick}>
      クリック
    </button>
  );
};

こちらは全て矢印関数で統一されています。

どちらも動作は同じですが、見た目の印象が違いますよね。

実は重要な違いがあります

見た目以外にも、実は重要な違いがいくつかあります。

一つずつ見ていきましょう。

1. ホイスティング(巻き上げ)の違い

まず、ホイスティングという仕組みの違いがあります。

// function宣言:ホイスティングされる
console.log(myFunction()); // 'Hello' が出力される
function myFunction() {
  return 'Hello';
}

function宣言は、コードの実行前に先に読み込まれます。 なので、宣言前でも使えるんです。

でも矢印関数は違います。

// 矢印関数:ホイスティングされない
console.log(myArrowFunction()); // エラーが発生
const myArrowFunction = () => {
  return 'Hello';
};

この場合、エラーが発生してしまいます。

矢印関数は、定義された後でしか使えません

ただし、Reactコンポーネントでは通常この違いは問題になりません。 コンポーネント内で適切に書けば、どちらも問題なく動作します。

2. thisの扱いの違い

次に、thisの扱いが違います。

// function宣言:独自のthisを持つ
function MyComponent() {
  this.name = 'Component';
  
  const handleClick = function() {
    console.log(this.name); // undefinedになる可能性
  };
  
  return <button onClick={handleClick}>クリック</button>;
}

function宣言では、関数ごとに独自のthisを持ちます。

でも矢印関数は違います。

// 矢印関数:thisを継承する
const MyComponent = () => {
  const name = 'Component';
  
  const handleClick = () => {
    console.log(name); // 正しく動作
  };
  
  return <button onClick={handleClick}>クリック</button>;
};

矢印関数は、周りのスコープからthisを引き継ぎます。

でも大丈夫です! 関数型コンポーネントでは、thisの違いはほとんど影響しません

3. 記述の簡潔性

矢印関数の方が、より簡潔に書けます

// function宣言:比較的冗長
function UserCard({ user }) {
  return (
    <div>
      <h3>{user.name}</h3>
      <p>{user.email}</p>
    </div>
  );
}

これを矢印関数で書くと、こうなります。

// 矢印関数:より簡潔
const UserCard = ({ user }) => (
  <div>
    <h3>{user.name}</h3>
    <p>{user.email}</p>
  </div>
);

returnを省略して、括弧で囲むだけで書けますね。

特に、シンプルなコンポーネントでは矢印関数の方がスッキリします。

React特有の考慮事項

Reactならではの考慮すべき点も見てみましょう。

1. React Developer Toolsでの表示

React Developer Toolsでは、コンポーネント名が重要です。

// function宣言:コンポーネント名が表示される
function UserProfile() {
  return <div>Profile</div>;
}

function宣言では、関数名がそのままコンポーネント名になります。

矢印関数でも同じです。

// 矢印関数:適切に名前を付ける
const UserProfile = () => {
  return <div>Profile</div>;
};

どちらも「UserProfile」として表示されます。

でも、これは避けてください。

// 無名関数は避ける
const UserProfile = function() {
  return <div>Profile</div>;
};

名前を付けることが重要です。

2. デフォルトエクスポートとの組み合わせ

エクスポートの書き方も見てみましょう。

// function宣言のエクスポート
function App() {
  return <div>App</div>;
}

export default App;

function宣言では、先に定義してから後でエクスポートします。

矢印関数でも同じです。

// 矢印関数のエクスポート
const App = () => {
  return <div>App</div>;
};

export default App;

でも、これは避けることをおすすめします。

// インライン(非推奨)
export default () => {
  return <div>App</div>;
};

インラインでのエクスポートは避ける方が良いです。

React Developer Toolsでコンポーネント名がわからなくなってしまいます。

パフォーマンス面での違い

「どっちの方が速いの?」と気になりますよね。

実は、パフォーマンス面での違いはほとんどありません。

1. 関数の作成タイミング

どちらも、再レンダリングのたびに関数が作成されます

// function宣言:再レンダリングのたびに関数が作成される
function MyComponent() {
  function handleClick() {
    console.log('クリック');
  }
  
  return <button onClick={handleClick}>クリック</button>;
}

矢印関数も同じです。

// 矢印関数:再レンダリングのたびに関数が作成される
const MyComponent = () => {
  const handleClick = () => {
    console.log('クリック');
  };
  
  return <button onClick={handleClick}>クリック</button>;
};

どちらも、パフォーマンス面では同じです。

2. useCallbackとの組み合わせ

パフォーマンスを最適化したい場合は、useCallbackを使います。

// useCallbackを使った最適化
const MyComponent = () => {
  const handleClick = useCallback(() => {
    console.log('クリック');
  }, []);
  
  return <button onClick={handleClick}>クリック</button>;
};

useCallbackを使うと、関数が再作成されるのを防げます。

function宣言でも同じように使えます。

// function宣言でも同様
function MyComponent() {
  const handleClick = useCallback(() => {
    console.log('クリック');
  }, []);
  
  return <button onClick={handleClick}>クリック</button>;
}

どちらを使っても、最適化の効果は同じです。

実際の使い分けのガイドライン

実際の開発では、どう使い分けるのが良いでしょうか?

具体的な例で見てみましょう。

1. コンポーネント定義では矢印関数がおすすめ

シンプルなコンポーネントでは、矢印関数がおすすめです。

// 推奨:矢印関数でスッキリと
const Header = () => (
  <header>
    <h1>サイトタイトル</h1>
  </header>
);

1行で書けて、とても読みやすいですね。

複雑な処理がある場合は、function宣言でも良いです。

// 複雑な処理がある場合
function UserDashboard({ user }) {
  const [loading, setLoading] = useState(false);
  const [data, setData] = useState(null);
  
  useEffect(() => {
    fetchUserData();
  }, []);
  
  const fetchUserData = async () => {
    setLoading(true);
    try {
      const response = await fetch(`/api/users/${user.id}`);
      const userData = await response.json();
      setData(userData);
    } finally {
      setLoading(false);
    }
  };
  
  return (
    <div>
      {loading ? <p>読み込み中...</p> : <UserData data={data} />}
    </div>
  );
}

この例では、function宣言を使っています。

複雑な処理がある場合は、どちらを使っても問題ありません。

2. イベントハンドラーは矢印関数で統一

イベントハンドラーは、矢印関数で統一することをおすすめします。

// 推奨:矢印関数で統一
const ContactForm = () => {
  const [email, setEmail] = useState('');
  const [message, setMessage] = useState('');
  
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('フォーム送信', { email, message });
  };
  
  const handleEmailChange = (e) => {
    setEmail(e.target.value);
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={handleEmailChange}
      />
      <textarea
        value={message}
        onChange={(e) => setMessage(e.target.value)}
      />
      <button type="submit">送信</button>
    </form>
  );
};

全てのイベントハンドラーを矢印関数で統一しています。

インライン(その場で書く)でも使えます。

3. カスタムフックでも矢印関数

カスタムフックでも、矢印関数で統一することをおすすめします。

// 推奨:矢印関数で統一
const useCounter = (initialValue = 0) => {
  const [count, setCount] = useState(initialValue);
  
  const increment = () => setCount(count + 1);
  const decrement = () => setCount(count - 1);
  const reset = () => setCount(initialValue);
  
  return { count, increment, decrement, reset };
};

カスタムフック内の関数も、全て矢印関数で書いています。

使用例も見てみましょう。

// 使用例
const Counter = () => {
  const { count, increment, decrement, reset } = useCounter(0);
  
  return (
    <div>
      <p>カウント: {count}</p>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
      <button onClick={reset}>リセット</button>
    </div>
  );
};

全て矢印関数で統一されていて、読みやすいですね。

現在のベストプラクティス

最後に、現在のベストプラクティスを紹介します。

1. 一貫性を保つことが最重要

プロジェクト全体で一貫したスタイルを使用することが重要です。

// プロジェクト全体で一貫したスタイルを使用
const App = () => {
  return (
    <div>
      <Header />
      <Main />
      <Footer />
    </div>
  );
};

const Header = () => <header>ヘッダー</header>;
const Main = () => <main>メイン</main>;
const Footer = () => <footer>フッター</footer>;

全てのコンポーネントで矢印関数を使っています。

チーム内で統一されていれば、どちらを使っても問題ありません。

2. ESLintでスタイルを統一

ESLintを使って、スタイルを自動的に統一できます。

// .eslintrc.json
{
  "rules": {
    "prefer-arrow-callback": "error",
    "func-style": ["error", "expression", { "allowArrowFunctions": true }]
  }
}

これらのルールを設定すると、矢印関数を優先的に使うようになります。

3. TypeScriptでの型定義

TypeScriptを使う場合の型定義も見てみましょう。

// TypeScriptでの型定義
type Props = {
  title: string;
  children: React.ReactNode;
};

const Card: React.FC<Props> = ({ title, children }) => (
  <div className="card">
    <h2>{title}</h2>
    {children}
  </div>
);

矢印関数でも、きちんと型定義できます。

function宣言でも同じです。

// function宣言でも同様
function Card({ title, children }: Props) {
  return (
    <div className="card">
      <h2>{title}</h2>
      {children}
    </div>
  );
}

どちらを使っても、型の恩恵は同じように受けられます。

よくある質問

初心者の方からよく聞かれる質問にお答えします。

Q1: パフォーマンスに違いはある?

A1: 関数型コンポーネントでは、パフォーマンスの違いはほとんどありません

どちらも同じように最適化されます。

心配する必要はありませんよ。

Q2: どちらが読みやすい?

A2: 矢印関数の方が簡潔で読みやすいとされることが多いです。

でも、これは個人の好みやチームの方針によります。

最も重要なのは、チーム内で統一されていることです。

Q3: 古いブラウザでの対応は?

A3: Babelでトランスパイルされるため、古いブラウザでも問題ありません。

モダンなReactアプリケーションでは、どちらを使っても大丈夫です。

安心して使ってください。

Q4: 初心者はどっちを使うべき?

A4: 矢印関数から始めることをおすすめします

現在のReactコミュニティでは、矢印関数が主流になっています。

チュートリアルや記事も、矢印関数を使ったものが多いです。

Q5: 混在させても問題ない?

A5: 技術的には問題ありませんが、統一することをおすすめします

コードの可読性と保守性を考えると、どちらか一方に統一した方が良いでしょう。

まとめ

お疲れさまでした!

ReactでのArrow Function vs Function宣言について、詳しく見てきました。

重要なポイントをまとめると、以下の通りです:

技術的な違い

  • ホイスティングの有無
  • thisの扱い方
  • 記述の簡潔性

実用的な観点

  • パフォーマンスの違いはほとんどない
  • どちらも正しく動作する
  • 一貫性が最も重要

現在のトレンド

  • 矢印関数が主流になっている
  • ESLintで統一できる
  • TypeScriptでもどちらも使える

おすすめの使い分け

  • コンポーネント定義:矢印関数
  • イベントハンドラー:矢印関数
  • カスタムフック:矢印関数
  • 複雑な処理:どちらでも可

最終的には、プロジェクト内での一貫性が最も重要です。

チームで決めたルールに従って、読みやすく保守しやすいコードを書いてください。

どちらを選んでも、適切に書かれたReactコンポーネントは正常に動作します。

ぜひ今回の知識を活用して、自分に合ったスタイルを見つけてくださいね。

まずは矢印関数から始めて、慣れてきたらチームのルールに合わせて調整していきましょう!

関連記事