1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Warning: React has detected a change in the order of Hooks called by X でハマる

Last updated at Posted at 2022-01-13

何をしていたか(悪いコード)

 React Native で、とあるアプリで、アカウント情報を表示する画面を作成していた。
 ReactNavigation を利用していた。

 処理の概要は以下

export default function MyPage {
  // ユーザ情報
  const [user, setUser] = useState();

  // ユーザ情報取得処理(あとで再利用する)
  const userCheck = async () => {
    const user = await getUser();
    setUser(user);
  }

  // 画面表示開始時に一度だけ読み取る
  useEffect(userCheck, []);

  // ユーザ情報がなければ処理中を表示
  if (!user) {
    return (
      <Text>処理中</Text>
    );
  }

  // ここから本画面 画面遷移用にnavigation 確保
  const navigation = useNavigation();

  return (
    <Text>本画面</Text>
    {/* 実際は、画面遷移などをする処理がある */}
  );
  

先に結論

 useNavigation も立派な Hook なので、条件によって呼ばれたり呼ばれなかったりするとこのエラーになります。

何が起きたか

 処理的には問題はないが、以下のエラーが表示されてビビる

Warning: React has detected a change in the order of Hooks called by MyPage.
This will lead to bugs and errors if not fixed.
For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks

   Previous render            Next render
   ------------------------------------------------------
1. useState                   useState
2. useState                   useState
3. useEffect                  useEffect
4. undefined                  useContext
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

何をしたか

 ひとまずURL https://reactjs.org/link/rules-of-hooks
 の記載があるので、皿の目で読む。
 翻訳などを使って、以下のように理解

Hooks は、レンダリング毎に、同じ順序、同じ量、同じものを毎回呼ばれるようにしなきゃいかんよ

上を読んでもハマる。どう考えたか

  • useState は毎度同じものが同じ順序で呼び出されるはず
  • useEffect も useState の後に呼び出されているし、👆のURL記載事項には反していないはず
  • あれ? useContext ってなんだっけ? 使ってたっけ?

結局何だったか

 useNavigation がHookであることを忘れていた。
 useNavigation は、内部で useContext を使っているので、この順序になっていた。
 state の条件により、 useNavigation が呼ばれたり呼ばれなかったりすると、このエラーになるようだ。

良いコード

export default function MyPage {
  // ユーザ情報
  const [user, setUser] = useState();

  // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  // 画面遷移用にnavigation 確保(条件に左右されず useNavigation は呼ばれるようにしないとエラーになるぞ)
  const navigation = useNavigation();
  // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■

  // ユーザ情報取得処理(あとで再利用する)
  const userCheck = async () => {
    const user = await getUser();
    setUser(user);
  }

  // 画面表示開始時に一度だけ読み取る
  useEffect(userCheck, []);

  // ユーザ情報がなければ処理中を表示
  if (!user) {
    return (
      <Text>処理中</Text>
    );
  }

  // ここから本画面
  return (
    <Text>本画面</Text>
    {/* 実際は、画面遷移などをする処理がある */}
  );
  

教訓

  • ライブラリをじゃんじゃん使うのはいいが、中で何をしているのか、特に Hooks については気を付けよう
  • use という言葉から、Hooks だと連想できるようになろう
  • 正常に動作するので、最悪放置しようと思ったけど、そんな自分にばいばいきん
1
0
1

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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?