s_kajigo
@s_kajigo (kaji shogo)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

コンポーネント内でのconsole表示について

解決したいこと

コンポーネント内の動きを見るためにconsole.logを使ってデバッグしたい。
状態によって表示、非表示とする動きをつかみたい。

現在react、railsを使って、認証機能について学習しています。
ルートにアクセスしたときに、Cookiesの情報を参照してrailsにログインユーザーがいないか問い合わせを行い、いればホームページへ、いなければルートからサインインページに飛ばせるような機能を目指しています。
いくつかの記事を参考にして以下のようにコードを記載しました。

App.js
import { createContext, useEffect, useState } from "react";
import { BrowserRouter, Switch, Route, Redirect } from "react-router-dom";
import { getCurrentUser } from "./api/auth";
import { Home } from "./components/Home";
import { SignIn } from "./components/SignIn";
import { SignUp } from "./components/SignUp";

export const AuthContext = createContext();

function App() {
  const [loading, setLoading] = useState(true);
  const [isSignedIn, setIsSignedIn] = useState(false);
  const [currentUser, setCurrentUser] = useState();

  // Cookie情報からログイン中ユーザーを取得、結果をハンドリング、最後にloadingをfalseに
  const handleGetCurrentUser = async () => {
    try {
      console.log("useEffectから呼ばれてgetCurrentUserします。");
      const res = await getCurrentUser();
      console.log(res);
      // res ? res.data.isLogin : undefined の省略
      if (res?.data.isLogin === true) {
        setIsSignedIn(true);
        setCurrentUser(res?.data.currentUser);
        console.log(res?.data.currentUser);
      } else {
        console.log("no current user");
      }
    } catch (e) {
      console.log(e);
    }
    setLoading(false);
  };

  //初回レンダリング、currentUserの変更時にユーザーを取得しにいく。
  useEffect(() => {
    console.log("useEffect実行");
    handleGetCurrentUser();
  }, [currentUser]);


  // loadingがfalseの時かつ、ログインユーザーが有効(isSignIn(true))のときにchildrenを表示。
  // ログイン中ユーザーがいなかったらサインインに飛ばす。
  console.log("privateの上の行です。");
  const Private = ({ children }) => {
    console.log("privateの中です。");
    if (!loading) {
      if (isSignedIn) {
        return children;
      } else {
        return <Redirect to="signin" />;
      }
    } else {
      return <></>;
    }
  };
  console.log("privateの下の行です。");
  return (
    <AuthContext.Provider
      value={{
        loading,
        setLoading,
        isSignedIn,
        setIsSignedIn,
        currentUser,
        setCurrentUser,
      }}
    >
      <BrowserRouter>
        <Switch>
          <Route exact path="/signup">
            <SignUp />
          </Route>
          <Route exact path="/signin">
            <SignIn />
          </Route>
          <Private>
            <Route exact path="/">
              <Home />
            </Route>
          </Private>
        </Switch>
      </BrowserRouter>
    </AuthContext.Provider>
  );
}

export default App;
auth.js
export const getCurrentUser = () => {
 if (
  !Cookies.get("_access_token") ||
  !Cookies.get("_client") ||
  !Cookies.get("_uid")
 )
  return console.log("ログイン中のユーザーはいません。");
 console.log("クッキーにデータがあることが確認できました。これを元にログイン情報がないかをrails側に問い合わせます。");
 return (
  client.get("/auth/sessions", {
   headers: {
    "access-token": Cookies.get("_access_token"),
    client: Cookies.get("_client"),
    uid: Cookies.get("_uid"),
   },
  })
 )
};

今回質問したい内容としては、
1、App.jsに記載したPrivateの中のconsole.logはなぜ表示されないのか?
2、Private内で分岐の条件としてloadingの真偽を使用しているが、初回のレンダリングでuseEffectが動くことにより、どの条件でもfalseとなるため、なぜこれが必要なのか?なぜelseで<>< />を返すのか?
です。

初歩的かつ、読みづらいコードかとは思いますが、ご教授いただけたら幸いです。

0

1Answer

1、
Privateが実行されたらconsole表示されると思います。
確認する際に、"/signup"と"/signin"以外のパスで実行しているでしょうか。
また、現時点のコンソール出力があるとより分かりやすいと思います。
念のために言っておきますが、console表示された場合、

"privateの上の行です。"
"privateの中です。"
"privateの下の行です。"

の順番にはなりません。なので、"privateの上の行です。"と"privateの下の行です。"の間だけ探しても見つかりませんよ。

2、
Privateの表示はloadingisSignedInの値に応じて変化します。

loading isSignedIn 表示結果
true - <></>
false true children
false false <Redirect to="signin" />

<></>・・・フラグメント<React.Fragment></>)の省略記法。
children・・・<Private></>の子要素。この場合は下記に該当。

<Route exact path="/">
    <Home />
</Route>

<Redirect />・・・<SignIn />へリダイレクト。

上記を踏まえた上で、loadingisSignedInに変化があった場合、Privateは再描画されて表示結果が変わります。
初回レンダリング時、loadingtrueなので、表示結果は<></>になりますが、
handleGetCurrentUserの非同期処理中、setLoadingsetIsSignedInの実行でloadingisSignedInの値が変わった場合、表示結果も新しい値に合わせて変化します。

<Redirect />の補足ですが、toプロパティにはパスを指定します。
<SignIn />へリダイレクトしたい場合は下記になります。

      if (isSignedIn) {
        return children;
      } else {
        // toプロパティにはパスを指定
-        return <Redirect to="signin" />;
+        return <Redirect to="/signin" />;
      }

最後に、質問と関係ありませんが、App.jsの中にはJSXコードかと思いますが、
コードブロックを先頭App.jsにすると、JavaScriptとして認識されます。
コードブロックの先頭をjsx:App.jsにすることで、ファイル名がApp.jsのJSXコードということを明示することができて、より正しいシンタックスハイライトで表示されます。読み手にとっても読みやすくなります。

1Like

Comments

  1. @s_kajigo

    Questioner

    回答ありがとうございます。
    1について、見直してみるとレンダリングのタイミングについて間違った認識をしており、上、下、中の順で表示されること確認できました。
    2について、わかりやすく表にまとめてくださりありがとうございます。
    最初のレンダリングではフラグメントが返され、非同期処理が終わるとstateに変化があるので再度レンダリングしてredirectされるか、コンポーネントを表示するか分岐するという理解をしたのですが、良いのでしょうか?
  2. @s_kajigo

    Questioner

    コードブロックについて、ご指摘ありがとうございます。今まで知らずに投稿してきてしましました。今後気を付けていこうと思います。
  3. 1、
    「上、下、中の順で表示されること確認できました。」
    よかったです!
    補足になりますが、中が下の後になるのは、実行タイミングの違いです。
    const Privateは宣言です。return内に記述して初めて実行されて、console表示されます。
    loadingとisSignedInの値が変わった際にも中のconsole表示されると思います。

    2、
    はい。ご認識の通りです。

    コードブロック
    過去の質問を拝見しました。
    スクリーンショットからコードブロックに、そして今回はシンタックスハイライトも利用するなど、すごく良くなってきたと思います。
    せっかく使うなら、と思い、指摘させていただきました。
  4. @s_kajigo

    Questioner

    返信ありがとうございます。
    今回、回答いただいた内容で知りたかったこと以上のものを学ばせていただきました!
    お教えいただき感謝します!

Your answer might help someone💌