環境
- 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
{
"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)
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から除外することで、プロジェクトレベルに巻き上げた。
"nohoist": [
"**/react-native",
- "**/react-native/**"
+ "**/react-native/!(react)/**"
]
まとめ
- yarn workspaceを使っている
- reactがnohoistの対象になっている(主にReact Nativeで起こる)
- ライブラリ内でReact Hooksを使っている
というかなり限定的な条件でしかこの問題は発生しませんが、ReduxもHooksを提供するっぽいので今後は似たようなハマり方が多くなるんじゃないかなと思いました。