react_on_railsでReact Hooksを使うと'Invalid hook call.'のエラーが出て動かない場合の解決策

1 min read読了の目安(約1700字 2

追記

コメントでご指摘いただいているように、こちらのエラーはreact_on_rails12.0.4で修正されているようですので、ライブラリのアップデートが可能であれば下記の対応は不要かもしれません。

はじめに

タイトル通りですが、react_on_railsでReact Hooksを使うと'Invalid hook call.'のエラーが出て動かない場合の解決策です。

React Hooksの文法が間違っていたり、ライブラリのバージョンが不適切だったりが原因ではなくて沼にはまりかけたので参考になれば幸いです。

エラーの詳細

React公式サイトの以下のエラーになります。

https://reactjs.org/docs/error-decoder.html/?invariant=321

公式の対応方針

React公式サイトの以下のページです。

https://reactjs.org/warnings/invalid-hook-call-warning.html

要約すると、
①ReactとReact DOMのバージョンに相違がないか?
②Hooksのルールを破っていないか?
③Reactが二重でインストールされていないか?
の3点です。

私の場合、以上の3点が原因ではなかったため解決しませんでした。

解決策

参考になったのは以下のreact_on_railsのissueです。

https://github.com/shakacode/react_on_rails/issues/1198

一部抜粋します。

I had the same problem which occurred when I tried to render a second component. Few hours of debugging later I realised that the problem was not related to having two components actually, but the fact that a root component (the one that I put in my view) was the one using useState hook.

As soon as I wrapped it in another component that didn't use the hook all was fine with the world again.

In other words, hooks for some reason can't be in root component that you are rendering with react_on_rails.

要約すると、react_on_railsが用意しているRails側のヘルパーメソッドで指定しているReactコンポーネント(ルートコンポーネント)でhooksを使うとこのエラーが発生するようです。

したがって、該当のルートコンポーネントをラップする、propsを中継するだけ(propsがあれば)のコンポーネントを作ればOKです。

# コード例
# NG
import React, { useState } from 'react'

const Example = props => {
  [example, setExample] = useState()
  ...
}

export default Example

# OK
import React, { useState } from 'react'

const ExampleRoot = props => {
  return <Example {...props} />
}

const Example = props => {
  [example, setExample] = useState()
  ...
}

export default ExampleRoot

おわりに

割と沼にハマりかけましたが、解決してよかったです。参考になれば幸いです。