LoginSignup
5
6

More than 3 years have passed since last update.

monorepoでReact Hooksを使うライブラリを使うとInvariant Violationエラーになった

Posted at

環境

  • react-native 0.59.5
  • react 16.8.3
  • apollo-client 2.4.6
  • react-apollo 2.5.4
  • react-apollo-hooks 0.4.5

yarn workspaceを使ってmonorepoとして運用している。

sampleProject
  ├── packages
  │   ├── core
  |   |   └── ...
  │   ├── app (React Native)
  |   |   ├── ...
  |   |   ├── package.json
  |   |   └── node_modules
  |   |       └── ...
  |   └── ...
  ├── package.json
  ├── yarn.lock
  └── node_modules
      └── ...

yarn workspace内でReact Nativeを動かすとき、プロジェクトレベルのnode_modulesにreact-nativeがインストールされてしまうと react-native コマンドが正常に動かない。yarn workspaceのnohoistの設定でreact-nativeを指定することでプロジェクトレベルではなくパッケージレベルのnode_modulesにインストールすることでこの問題を解決している。

参考: nohoist in Workspaces | Yarn Blog

package.json
{
  "name": "sampleProject",
  "private": true,
  "workspaces": {
    "packages": [
      "packages/*"
    ],
    "nohoist": [
      "**/react-native",
      "**/react-native/**"
    ]
  }
}

発生した問題

react-apollo-hooks を使おうとすると、Function Component内で使用したにも関わらず以下のようなエラーになる。

Invariant Violation: Hooks can only be called inside the body of a function component. (https://fb.me/react-invalid-hook-call)
App.tsx
import React, { useState } from 'react';
import { useQuery } from 'react-apollo-hooks';

...

interface Props {}

const App: React.FC<Props> = props => {
  // Invariant Violationが発生する
  const { data, error, loading } = useQuery(SOME_QUERY);

  // こちらはエラーにならない
  const [count, setCount] = useState(0);

  ...
}

export default App;

原因と解決法

react モジュールが複数存在してしまっていたのが原因だった。
https://github.com/facebook/react/issues/13991
https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate-react

nohoistにreact-nativeを指定したことで、react-nativeが使用する react がパッケージレベルに、react-apollo-hooksが使用する react はプロジェクトレベルに存在する状態になっていた。

sampleProject
  ├── packages
  │   ├── app
  |   |   ├── ...
  |   |   └── node_modules
  |   |       ├── react <- パッケージレベル
  |   |       ├── react-native
  |   |       └── ...
  |   └── ...
  ├── node_modules
  |   ├── react <- プロジェクトレベル
  |   ├── react-apollo-hooks
  |   └── ...
  └── ...

これを解決するために、issueのコメントのようにreact-nativeが依存するreactをnohoistから除外することで、プロジェクトレベルに巻き上げた。

package.json
     "nohoist": [
       "**/react-native",
-      "**/react-native/**"
+      "**/react-native/!(react)/**"
     ]

まとめ

  1. yarn workspaceを使っている
  2. reactがnohoistの対象になっている(主にReact Nativeで起こる)
  3. ライブラリ内でReact Hooksを使っている

というかなり限定的な条件でしかこの問題は発生しませんが、ReduxもHooksを提供するっぽいので今後は似たようなハマり方が多くなるんじゃないかなと思いました。

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6