見出し画像

ボタンをクリックしたら画面が真っ白になった話(React Hooks編)

この記事に書いてあること

Reactでnpm startはエラーもでずにできるけど、ボタンをクリックしたりすると突如画面が真っ白になった&&コンソールには身に覚えのないスタックトレースと、エラーメッセージに
Hooks can only be called inside the body of a function component.

Warning: React has detected a change in the order of Hooks called by ~
という謎のエラーが出ている
という状態に陥った人(≒過去の自分)に向けた解決のヒントです。

ほ、ほ、Hooks

Reactではuse~で始まる関数のことをHooks(フックス)と呼ぶようです。use~のAPIの例はこんな感じ。

  •  useState

  • useSelector

"Hooks can only~"のエラーメッセージが出る理由は3つあるらしいのですが、今回は上のAPIを使用するルール違反に関して書きます。
ちなみに、この記事に書いてある大体のことはエラーメッセージ中に書かれているURLに記載されているのですが、英語読むのを嫌がった結果数時間はまったので、まとめを残しておきます🥺

Hooksのルール違反

Hooksは以下の場所で使ってはならないようです。

  1. 条件文やループの中

  2. Hooksの前で条件によってreturnが呼ばれる場合

  3. イベントハンドラーの中

  4. クラスコンポーネントの中

  5. useMemo、useReducer、useEffectの中

4.のクラスコンポーネントに関しては、React公式がクラスコンポーネントより関数コンポーネントをお勧めしているので、関数コンポーネントを使うのが安定なんだと思います。
今回 僕がはまったのは3.でした。react-reduxのconnect()を使っている場合、適当なサンプルを参照しているとはまるかもしれないので次に具体例を書きます。

良くない例と解決方法

コードはこんな感じでした。

const mapStateToProps = (state: RootState) => {
  return { hoge: useSelector(hogeSelector) }
}

export default connect(mapStateToProps)(Hoge)

この例では、connect()に渡すコールバック関数の中でuseSelectorを使っているだけなのでルール違反なのか?という感じでした。
ただ、このmapStateToProps は
別のコンポーネントのclickイベント→state変化→useSelector実行
という呼ばれ方をする実装だったため、巡り巡ってイベントハンドラの中判定されたのかな?という感じでした。(自信はない)
解決方法は簡単でuseSelectorを使わないようにするだけです。

const mapStateToProps = (state: RootState) => {
  return { hoge: hogeSelector(state) }
}

export default connect(mapStateToProps)(Hoge)

あとがき

この記事を書いているときに色々調べてたら公式が
「connectは8.x系まではサポートするけど、Hooks使ってね😅」
って書いているのを見つけて、connect()とHooks両方を使う中途半端なことしていた結果の事故であることがわかりました🥺
とりあえずconnect()使わない版で自分のコードは書き直そうと思いました🍣


この記事が気に入ったらサポートをしてみませんか?