React Hooksは、状態や副作用を管理する簡潔かつ強力な機能です。Reactでの関数コンポーネント記述方法に革命がもたらされました。

しかし、どんな新しい機能でもそうですが、それには独自のルールとガイドラインが伴います。React開発者が遭遇する可能性のあるエラーとして、「react hooks must be called in a react function component or a custom react hook function」があります。

今回の記事では、このエラーの詳細を掘り下げ、なぜ発生するのかを理解し、解決のためのベストプラクティスをご紹介します。

「React Hooks Must Be Called In a React Function Component or a Custom React Hook Function」エラーの原因とは

React Hooksの使用にはルールがあります。多くのReact開発者が、Reactを学ぶ際にここから逸脱してしまい、それがエラーにつながります。よくあるエラーの1つが「react hooks must be called in a react function component or a custom react hook function」です。

このエラーは、大きく分けて2つのことを行うと発生します。

  • クラスコンポーネント内でReact Hooksを使用する
  • ネストされた関数内でReact Hooksを呼び出す

useStateuseEffectuseContextなどのReact Hooksは、関数コンポーネントまたはカスタムフック関数のトップレベルで呼び出されるように設計されています。また、関数コンポーネントの中だけで呼び出すべきで、クラスコンポーネントでは呼び出すべきではありません。これらはReact Hooksの2つの主要なルールであり、ReactコンポーネントでHooksが正しく一貫して使用されることを保証します。

Hooksが無効な場所で呼び出されると、「react hooks must be called in a react function component or a custom react hook function」というエラーが発生します。このESLintエラーは、ReactアプリケーションでHooksが予期せぬ動作やバグを引き起こすのを防ぐために存在します。

「React Hooks Must Be Called In a React Function Component or a Custom React Hook Function」エラーを解決する2つの方法

このエラーは、React Hookの使い方を間違えた状況に応じて、いくつかの方法で解決することができます。

1. クラスコンポーネントでReact Hooksを呼び出さない

フックは、関数コンポーネントまたはカスタムフックでのみ動作するように設計されています。というのも、コンポーネントの状態とライフサイクルを管理するために、関数コンポーネントのコールスタックを使用するからです。クラスコンポーネントにはこのコールスタックがないため、クラスコンポーネントで直接フックを使用することはできません。

import React, { useState } from 'react';
class Counter extends React.Component {
    state = { count: 0 };
    render() {
        const [count, setCount] = useState(0);
        // エラー:React Hooks must be called in a React function component or a custom React Hook function
        return (
            <div>
                <p>You clicked {count} times</p>
                <button onClick={() => setCount(count + 1)}>Click me</button>
            </div>
        );
    }
}
export default Counter;

このコードを使用すると、次のようなエラーが発生します。

エラー
エラー

これを解決するには、いくつかの方法があります。クラスコンポーネントでstatesetStateを使うか、コンポーネントを関数コンポーネントに変換するか、Higher-Order Component (HOC) を使うことができます。

A. クラスコンポーネントを関数コンポーネントに変換する

Hooksが機能するためには、関数コンポーネントを使用する必要があります。これは、Hooksが関数コンポーネントで動作するように設計されているためです。

以下の例では、先ほどのクラスコンポーネントを関数コンポーネントに変換しています。

import { useState } from 'react';

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

B. 高階コンポーネント(HOC)を使用する

クラスコンポーネントでHooksを使用する方法の1つに、Higher-Order Component(HOC)があります。

HOCは、コンポーネントを受け取り、追加のpropsまたは機能を持つ新しいコンポーネントを返す関数です。以下の例では、Hooksが機能するようにHOCを使用しています。

import React, { useState } from 'react';

function withHooks(WrappedComponent) {
  return function(props) {
    const [count, setCount] = useState(0);
    return (
      < WrappedComponent count={count} setCount={setCount} {...props} />
    );
  };
}

class MyComponent extends React.Component {
  render() {
    const { count, setCount } = this.props;
    return (
      < div>
        < p>Count: {count}< /p>
        < button onClick={() => setCount(count + 1)}>Increment< /button>
      < /div>
    );
  }
}

export default withHooks(MyComponent);

C. クラスコンポーネントでstateを使う

最後に、コンポーネントの構文を変更したくないとします。useState Hookの代わりに、statesetStateを使うことができます。

import React from 'react';

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  handleIncrement = () => {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.handleIncrement}>Increment</button>
      </div>
    );
  }
}

export default MyComponent;

2. ネストされた関数内でReact Hooksを呼び出さないようにする

React Hooksの明確なルールとして、すべてのHooksは関数コンポーネントまたはカスタムフック関数のトップレベルで呼び出す必要があります。ネストした関数の中でHookを呼び出すと、このルールに反してしまいます。

以下では、useStateHookがhandleClick関数の中で呼び出されています。

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

export default MyComponent;

このままでは、以下のエラーが発生します。

エラーの例
エラーの例

そこで、Hookを関数の外、つまり関数コンポーネントのトップレベルに移動することでエラーに対処します。

import React, { useState } from 'react';

function MyComponent() {
    const [count, setCount] = useState(0);

    function handleClick() {
        setCount(count + 1);
    }

    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={handleClick}>Increment</button>
        </div>
    );
}

export default MyComponent;

まとめ

この記事では、「react hooks must be called in a react function component or a custom react hook function」エラーの原因とその対処法をご紹介しました。

React Hooksを使用する際には、このようなエラーが発生しないように、ルールに従い作業するようにしましょう。

Reactアプリにぴったりのホスティングソリューションをお探しなら、Kinstaのアプリケーションホスティングがおすすめです。無料利用枠をご利用いただけます。

このエラーを経験したことはありますか?どのように解決しましたか?この記事にない他の方法を使用したでしょうか。以下のコメント欄でお聞かせください。