環境
- 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)
```
```tsx: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://github.com/facebook/react/issues/13991)
[https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate-react](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のコメント](https://github.com/facebook/react/issues/13991#issuecomment-488085464)のようにreact-nativeが依存するreactをnohoistから除外することで、プロジェクトレベルに巻き上げた。
```diff:package.json
"nohoist": [
"**/react-native",
- "**/react-native/**"
+ "**/react-native/!(react)/**"
]
```
## まとめ
1. yarn workspaceを使っている
2. reactがnohoistの対象になっている(主にReact Nativeで起こる)
3. ライブラリ内でReact Hooksを使っている
というかなり限定的な条件でしかこの問題は発生しませんが、[ReduxもHooksを提供するっぽい](https://react-redux.js.org/next/api/hooks)ので今後は似たようなハマり方が多くなるんじゃないかなと思いました。