React Hooks使って『Hooks can only be called inside the body of a function component.』って怒られる問題

まだalphaリリース(v16.7.0-alpha)のHooksを使っていたらハマった。

『Hooks can only be called inside the body of a function component.』とエラーバウンダリーの画面で怒られてしまう。

状況

  • 親プロジェクトでHooksを用いたライブラリコンポーネントを作成している
  • 作っているライブラリコンポーネントを子プロジェクトにlinkして子プロジェクト内で試用しようとした

怒られる。

ディレクトリ図はこういう感じで、ライブラリ開発現場(親)の中にexample(子)ディレクトリがあって、子は子で別途package.jsonnode_modulesを抱えているという状況。

.
├── example
│   ├── node_modules
│   ├── package.json
│   └── src
├── node_modules
├── package.json
└── src

なおexampleディレクトリはライブラリのデモページのためのディレクトリ。

この構成は

このcreate-react-libraryというボイラープレートの構成をベースにしている。

Issue

github.com

Dan氏が想定の挙動だよ〜ってコメントしている

エラーの原因はライブラリが参照しているReactと子ディレクトリで参照しているReactが異なるためらしい。

  • ディレクトリで親階層にあるライブラリを利用はするけど作成中のライブラリにLinkを貼っているだけ
    • ライブラリが参照するのは/node_modules/react
  • ディレクトリのプロジェクト自体が利用するreactはどこ
    • /example/node_modules/react

ようは参照しているreactのインスタンスが異なるとダメっぽい。

解決方法

github.com

このIssueに解決策が書かれていて、子のpackage.jsondependenciesにあるReactを親のnode_modulesに指定してやればよい

  "dependencies": {
    .
    .
    .
    "react": "link:../node_modules/react",
    "react-dom": "link:../node_modules/react-dom"
  }

これでyarn -fあるいはrm -rf node_modules/ && yarnてな具合に依存モジュールを再インストールすればOK

子プロジェクトでのReactは親階層である/node_modules/reactを参照するようになるので、同一インスタンスのものが参照されるようになって解決。

lerna あるいは yarn workspace でも解決できそう

参照するReactのインスタンスが異なるのが原因なので、それを回避できればOKっぽい。

lernayarn workspaceを用いると単一の共通したnode_modulesを利用できるので、解決できるはず。

実際自分も最初は↑の単純に親階層にlinkさせる方法にたどり着けなくてyarn workspaceを試していた。

手元でHooksの動作まではうまく行ったのだけど、自分の環境ではRollup.jsでバンドルしていた箇所がどうも壊れてしまって別問題をはらんでしまったのでやめた。(おそらくnode_modulesが複数箇所に分散されるのでそれをRollup.jsが解決できずでなにかしらコケてしまった)

rollup+yarn workspaceでうまくいったよ〜っていう人がいれば教えて欲しい。


余談あるいはバージョニング問題

余談だけど、本件でハマっているときに出くわした別問題で、Reactのv16.7.0にはHooks機能が削られているという罠があった。

  "dependencies": {
    "react": "^16.7.0-alpha.2",
    "react-dom": "^16.7.0-alpha.2"
  }

最初Hooksを試すためにalpha2をインストールしていて、exactオプションなしでインストールしたからチルダ(^)ありになっていた。

semver的にこの状態でyarn upgradeするとv16.7.0のStableに更新されてしまうけど、なんとv16.7.0ではHooksはごっそり削られていた。

  • v16.7.0-alpha.2
    • Hooksあり
  • v16.7.0
    • Hooksなし

Stableリリースでたしかに『Hooksはまだ』とは言われていたけど、alphaの内容継承していると考えるのがなんとなく自然なはずで、upgradeしてから急にHooksが動かなくなってハマってしまった。

yarn add --exact react@16.7.0-alpha.2 react-dom@16.7.0-alpha.2でexact指定しておく必要があった。

こうするとチルダなしでpackage.jsonに書き込まれるので、依存関係のアップグレードをしてもReactがStableに上がることはない。


いろいろ大変という気持ち。

それはそうと見よう見まねで"link: ~"って書いてるけどこれのリファレンスが見つけられない。"file: ~"のリファレンスなら出てくるけど違いが知りたい。