You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NextJS 13 如何实现页面完全加载后再显示

问题描述

我是NextJS前端新手,正在进行功能练习。希望实现页面在所有资源(包括所有HTTP请求获取响应后)加载完成后再显示。具体场景:使用Magic Link进行身份验证,希望导航栏直接显示已登录用户的邮箱/用户名,避免先显示硬编码的'anonymous'再延迟更新为实际邮箱的情况。

现有代码如下:

首页 page.js

import Banner from "@/components/banner/banner";
import NavBar from "@/components/navbar/navbar";
import CardSections from "@/components/card-sections/card-sections";
import {getVideos} from "@/libs/videos";

async function getVideosApi() {
  return getVideos()
}

export default async function Home() {
  const disneyVideos = await getVideosApi()

  return (
      <main className={'main'}>
        <NavBar/>
        <Banner title={'Titanic - the movie'} subTitle={'Sad story about Titanic'} imgUrl={'/static/titanic.webp'}/>
        <CardSections title={'Disney'} videos={disneyVideos}></CardSections>
      </main>
  )
}
"use client"

import styles from "./navbar.module.css";
import {useRouter} from "next/navigation";
import Link from "next/link";
import {useEffect, useState} from "react";
import {magic} from "@/app/login/magic-link";

export default function NavBar() {
  const [showDropdown, setShowDropdown] = useState(false)
  const [username, setUsername] = useState("anonymous")

  const router = useRouter();

  useEffect(() => {
    async function getUsername() {
      try {
        const { email } = await magic.user.getMetadata();
        if (email) {
          console.log(email);
          setUsername(email);
        }
      } catch (error) {
        console.log("Error retrieving email:", error);
      }
    }
    getUsername();
  }, []);

  const handleOnClickHome = (e) => {
    e.preventDefault();
    router.push('/')
  }

  const handleOnClickMyList = (e) => {
    e.preventDefault();
    router.push('/browse/my-list')
  }

  const handleDropdown = (e) => {
    e.preventDefault();
    setShowDropdown(!showDropdown)
  }

  return (
      <div className={styles.container}>
        <div className={styles.wrapper}>
          <a className={styles.logoLink}>
            <div className={styles.logoWrapper}>Netflix</div>
          </a>

          <ul className={styles.navItems}>
            <li className={styles.navItem} onClick={handleOnClickHome}>Home</li>
            <li className={styles.navItem} onClick={handleOnClickMyList}>My List</li>
          </ul>
          <nav className={styles.navContainer}>
            <div>
              <button onClick={handleDropdown} className={styles.usernameBtn}>
                <p className={styles.username}>{username}</p>
              </button>
              {showDropdown && <div>
                <Link href={'/login'}>Sign In
                </Link>
              </div>
              }
            </div>
          </nav>
        </div>
      </div>
  );
};
import {Magic} from 'magic-sdk';

const createMagic = (key) => {
  return typeof window !== 'undefined' && new Magic(key);
};

export const magic = createMagic("pk_live_*****");

解决方案

由于Magic SDK依赖浏览器环境(需要window对象),无法在Next.js服务器组件中直接获取用户信息,所以需要在客户端组件中处理加载状态,避免显示临时的'anonymous'文本。以下提供两种可行方案:

方案一:手动控制加载状态

修改NavBar组件,将初始用户名设为null,并添加加载状态标记,直到用户信息获取完成后再显示实际内容:

"use client"

import styles from "./navbar.module.css";
import {useRouter} from "next/navigation";
import Link from "next/link";
import {useEffect, useState} from "react";
import {magic} from "@/app/login/magic-link";

export default function NavBar() {
  const [showDropdown, setShowDropdown] = useState(false)
  const [username, setUsername] = useState(null)
  const [isLoading, setIsLoading] = useState(true)

  const router = useRouter();

  useEffect(() => {
    async function getUsername() {
      try {
        const { email } = await magic.user.getMetadata();
        // 未登录时显示"Sign In"而非"anonymous"
        setUsername(email || "Sign In");
      } catch (error) {
        console.log("Error retrieving email:", error);
        setUsername("Sign In");
      } finally {
        // 无论请求成功或失败,结束加载状态
        setIsLoading(false);
      }
    }
    getUsername();
  }, []);

  // 加载中时显示加载状态的导航栏
  if (isLoading) {
    return (
      <div className={styles.container}>
        <div className={styles.wrapper}>
          <a className={styles.logoLink}>
            <div className={styles.logoWrapper}>Netflix</div>
          </a>
          <ul className={styles.navItems}>
            <li className={styles.navItem} onClick={(e) => {e.preventDefault(); router.push('/')}}>Home</li>
            <li className={styles.navItem} onClick={(e) => {e.preventDefault(); router.push('/browse/my-list')}}>My List</li>
          </ul>
          <nav className={styles.navContainer}>
            <div>
              <button className={styles.usernameBtn} disabled>
                <p className={styles.username}>Loading...</p>
              </button>
            </div>
          </nav>
        </div>
      </div>
    );
  }

  const handleOnClickHome = (e) => {
    e.preventDefault();
    router.push('/')
  }

  const handleOnClickMyList = (e) => {
    e.preventDefault();
    router.push('/browse/my-list')
  }

  const handleDropdown = (e) => {
    e.preventDefault();
    setShowDropdown(!showDropdown)
  }

  return (
      <div className={styles.container}>
        <div className={styles.wrapper}>
          <a className={styles.logoLink}>
            <div className={styles.logoWrapper}>Netflix</div>
          </a>

          <ul className={styles.navItems}>
            <li className={styles.navItem} onClick={handleOnClickHome}>Home</li>
            <li className={styles.navItem} onClick={handleOnClickMyList}>My List</li>
          </ul>
          <nav className={styles.navContainer}>
            <div>
              <button onClick={handleDropdown} className={styles.usernameBtn}>
                <p className={styles.username}>{username}</p>
              </button>
              {showDropdown && (
                <div>
                  <Link href={'/login'}>Sign In</Link>
                </div>
              )}
            </div>
          </nav>
        </div>
      </div>
  );
};

方案二:使用React Suspense异步加载用户信息

将用户信息的获取逻辑封装成独立的异步客户端组件,通过Suspense组件实现自动加载状态管理:

1. 创建异步用户信息组件

// components/UserInfo.js
"use client"

import {magic} from "@/app/login/magic-link";

export default async function UserInfo() {
  try {
    const { email } = await magic.user.getMetadata();
    return email || "Sign In";
  } catch (error) {
    console.log("Error retrieving email:", error);
    return "Sign In";
  }
}

2. 修改NavBar组件,引入Suspense

"use client"

import styles from "./navbar.module.css";
import {useRouter} from "next/navigation";
import Link from "next/link";
import {useState, Suspense} from "react";
import UserInfo from "./UserInfo";

export default function NavBar() {
  const [showDropdown, setShowDropdown] = useState(false)

  const router = useRouter();

  const handleOnClickHome = (e) => {
    e.preventDefault();
    router.push('/')
  }

  const handleOnClickMyList = (e) => {
    e.preventDefault();
    router.push('/browse/my-list')
  }

  const handleDropdown = (e) => {
    e.preventDefault();
    setShowDropdown(!showDropdown)
  }

  return (
      <div className={styles.container}>
        <div className={styles.wrapper}>
          <a className={styles.logoLink}>
            <div className={styles.logoWrapper}>Netflix</div>
          </a>

          <ul className={styles.navItems}>
            <li className={styles.navItem} onClick={handleOnClickHome}>Home</li>
            <li className={styles.navItem} onClick={handleOnClickMyList}>My List</li>
          </ul>
          <nav className={styles.navContainer}>
            <div>
              <button onClick={handleDropdown} className={styles.usernameBtn}>
                <Suspense fallback={<p className={styles.username}>Loading...</p>}>
                  <p className={styles.username}><UserInfo /></p>
                </Suspense>
              </button>
              {showDropdown && (
                <div>
                  <Link href={'/login'}>Sign In</Link>
                </div>
              )}
            </div>
          </nav>
        </div>
      </div>
  );
};

内容的提问来源于stack exchange,提问作者ROZZ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 15:42:07