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

【React / Chacra-ui v3.x.x】遷移成功時にToastを実行する方法

0
Last updated at Posted at 2025-11-24

環境

  • React 19.2.0
  • @chakra-ui/react 3.29.0
  • vite 7.2.2

実現方法

  • 遷移にはReactのhooksの一つuseNavigate()を利用
  • toastの利用は公式を参照 公式
  • 遷移条件としては無料のREST APIであるJSONPlaceholderを利用
    Loginページの入力フィールドでの入力値がAPI上に存在すれば/Homeに遷移してtoastで"成功"と表示

うまくいかない例

login関数のthenで記載の通り、toastのロジックを記載したshowMessage関数の直後にnavigate('/Home')をコードする方法です。
この方法は結果として遷移に成功するものの、toastは実行されません。
showMessage()関数内部のconsole.log結果が正しく表示されるため実行されています。
しかし、遷移がただちに実行されることでtoastを確認することはできません。

Login.tsx

/*
Loginページの入力フィールドにidを入力してボタンをクリックする。
クリック時にuseAuth.tsxで定義しているlogin関数を実行
**/

import { Button,Input } from "@chakra-ui/react";
import { memo, useState, type ChangeEvent } from "react";
import { Toaster } from "../../components/ui/toaster";
import { useAuth } from "../../hooks/useAuth";

export const Login = memo(() => {
  const { login } = useAuth();
  const [inputId, setInputId] = useState<string>("");
  const onChangeInput = (event: ChangeEvent<HTMLInputElement>) => {
    setInputId(event.target.value);
  };

  return (
    <Input
      placeholder="ユーザーID"
      type="text"
      value={inputId}
      onChange={onChangeInput}
    />
    <Button onClick={() => login(inputId)}>
      ログイン
    </Button>
    <Toaster />
  );
});
useAuth.tsx
import axios from "axios";
import type { User } from "../types/api/user";
import { useNavigate } from "react-router-dom";
import { useCallback, useState } from "react";
import { toaster } from "../components/ui/toaster";

export const useAuth = () => {
  //navigateのHooksを使用
  const navigate = useNavigate(); 
  
  //toastの作成
  const showMessage = useCallback((result: string) => {
    toaster.create({
      title: `Toast status is ${result}}`,
      duration:2000,
    });
    console.log("showMessage");
  }, []);
  
  const login = useCallback((id: string) => {
  //APIを利用して値を取得
    axios
      .get<User>(`https://jsonplaceholder.typicode.com/users/${id}`)
      .then((res) => {
          //toastを表示させたのちにnavigateで遷移する
          showMessage("成功"); 
          navigate("/Home");
      })
      .catch(() => {
        showMessage("失敗");
      })
      .finally(() => {});
  }, []);
  return { login };
};

解決策

解決策としては2通り考えられる。(下記のstackoverflow記事を参考)

  1. 遷移元でtoastの表示を待った後に遷移を実行させる
  2. 遷移元ではなく遷移先でtoastの表示を実行する

今回本記事では2番の方法で解説いたします。

うまくいく例(解決策2のコード)

  • 遷移元でstateを引数で渡す(toastでの表示文字列が固定ならば不要)
  • 遷移先でtoastの表示にはとPromise.resolve().then(callback)を利用する
login.tsx
// 変更なし
useAuth.tsx
  //変更点のみ記載
  
  /*useAuthでは記載しない. 定義したshowMessage関数も削除 **/
  //showMessage("成功");

  //遷移先で参照できるようにstateに保存
  navigate("/Home", { state: { message: "成功" } });
Home.tsx
import { memo, useCallback, useEffect } from "react";
import { useLocation } from "react-router-dom";
import { toaster, Toaster } from "../../components/ui/toaster";

export const Home = memo(() => {
  //Login.tsxで保存したstateの取り出し
  const location = useLocation();

  //showMessage関数をこちらで定義する.hooksとして切り出してもよい。
  const showMessage = useCallback((result: string) => {
    toaster.create({
      title: `Toast status is ${result}}`,
      duration: 2000,
    });
    console.log("showMessage");
  }, []);

  //ここが重要
  useEffect(() => {
    Promise.resolve().then(() => {
      showMessage(location.state.message);
    });
  }, []);

  return (
    <>
      <p>Homeページです</p>
      <Toaster />
    </>
  );
});

なぜPromise.resolve().then(callback)が必要か

こちらを使用しない場合、下記のようなエラーがログ上で表示されます。
※Chome環境において上記を書かなくてもtoast自体は正しく機能します。

Home.tsx:10 flushSync was called from inside a lifecycle method. React cannot flush when React is already rendering. Consider moving this call to a scheduler task or micro task.

エラーの内容を見るとレンダリング実行時にライブラリの関数(flushSync)が意図しない形で呼び出されているように見えます。
最後の一文のアドバイスの通り、処理が終わった後にshowMessage関数を呼び出す工夫が必要そうです。
javascriptはコードを非同期的に処理していくため(関数の処理完了をまたずに次の処理に進んでいく)ため生じると考えられます。

これを実現するために、コードでも記載しているPromise.resolve.then(callback)を利用します。このcallbackはすべてマイクロタスクと呼ばれ、その他の処理完了後に呼び出されるものです。
この内容についてはPADAoneさんのタスクキューとマイクロタスクキューを参考にしております。

マイクロタスク自体は、それを呼び出し関数やプログラムが実行された後にコールスタックが空になった後にのみ実行される短い関数です。API や Promise の then() メソッドなどの引数に渡すコールバック関数がマイクロタスクとして扱われます。
Promise.resolve().then(callback1).catch(callback2).finally(callback3) のように、Promise のプロトタイプメソッドである、then(), catch(), finally() の引数に渡すコールバック関数はすべてマイクロタスクとしてマイクロタスクキューへと送られます。

まとめ

Chacra-ui v2からv3にかけて大きな変更があったため、v2では問題なく動くにもかかわらずv3では同じ書き方だと動かないということに多く遭遇します。ただ単純にv2での書き方をv3での書き方に置換するだけだとハマるポイントが多いため、エラーメッセージをヒントに解決することが重要になりそうです。

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