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

Astro中React组件无法运行,无法访问环境变量求助

问题:Astro中React Navbar组件无响应且Supabase环境变量错误

问题现象

  • 移动端菜单按钮点击无响应
  • 页面加载时抛出错误:
    [Error] [astro-island] Error hydrating /src/components/Navbar.tsx Error: Missing Supabase URL or anon key Module Code — supabase.ts:7 (anonymous function) (localhost:1171:3738)
    
import { useState, useEffect } from "react";
import { Menu, X, User, LogOut } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { supabase } from "@/lib/supabase";

export const Navbar = () => {
  const [open, setOpen] = useState(false);
  const [loggedIn, setLoggedIn] = useState(false);

  const checkUser = async () => {
    const session = await supabase.auth.getSession();
    if (session) {
      setLoggedIn(true);
    } else {
      setLoggedIn(false);
    }
  };

  checkUser();

  return (
    <nav className="bg-white border-b sticky top-0">
      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
        <div className="relative flex items-center justify-between h-16">
          <div className="flex-1 flex items-center justify-start sm:items-stretch sm:justify-start">
            <div className="flex-shrink-0">
              <a href="/">
                <img className="h-8 w-auto" src="/favicon.svg" alt="Workflow" />
              </a>
            </div>
            <div className="hidden sm:block sm:ml-6">
              <div className="flex space-x-2">
                <a
                  href="#"
                  className="text-gray-900 hover:text-gray-500 hover:text-white px-3 py-2 text-sm"
                >
                  Stories
                </a>
                <a
                  href="#"
                  className="text-gray-900 hover:text-gray-500 hover:text-white px-3 py-2 text-sm"
                >
                  Writers
                </a>
                <a
                  href="#"
                  className="text-gray-900 hover:text-gray-500 hover:text-white px-3 py-2 text-sm"
                >
                  Blog
                </a>
                <a
                  href="#"
                  className="text-gray-900 hover:text-gray-500 hover:text-white px-3 py-2 text-sm"
                >
                  About
                </a>
              </div>
            </div>
          </div>
          <div className="flex items-center space-x-2">
            {!loggedIn && (
              <>
                <Button className="hidden sm:inline-block" variant="outline">
                  Login
                </Button>
                <Button className="hidden sm:inline-block">Sign Up</Button>
              </>
            )}
            {loggedIn && (
              <DropdownMenu>
                <DropdownMenuTrigger>
                  <Avatar className="h-8 w-8">
                    <AvatarImage src="https://github.com/shadcn.png" />
                    <AvatarFallback>CN</AvatarFallback>
                  </Avatar>
                </DropdownMenuTrigger>
                <DropdownMenuContent>
                  <a href="/profile">
                    <DropdownMenuItem>
                      <User />
                      My Profile
                    </DropdownMenuItem>
                  </a>
                  <DropdownMenuItem>
                    <LogOut />
                    Log out
                  </DropdownMenuItem>
                </DropdownMenuContent>
              </DropdownMenu>
            )}
            <div className="sm:hidden">
              <button
                type="button"
                className="inline-flex items-center justify-center text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"
                aria-controls="mobile-menu"
                aria-expanded={open ? "true" : "false"}
                onClick={() => setOpen(!open)}
              >
                <span className="sr-only">Open main menu</span>
                {open ? (
                  <X className="block h-6 w-6" aria-hidden="true" />
                ) : (
                  <Menu className="block h-6 w-6" aria-hidden="true" />
                )}
              </button>
            </div>
          </div>
        </div>
      </div>

      <div
        className={`sm:hidden ${open ? "block" : "hidden"}`}
        id="mobile-menu"
      >
        <div className="px-2 pt-2 pb-3 space-y-1">
          <a
            href="#"
            className="text-gray-900 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"
          >
            Dashboard
          </a>
          <a
            href="#"
            className="text-gray-900 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"
          >
            Team
          </a>
          <a
            href="#"
            className="text-gray-900 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"
          >
            Projects
          </a>
          <a
            href="#"
            className="text-gray-900 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"
          >
            Calendar
          </a>
          <div className="mt-4 space-y-1">
            <Button className="w-full" variant="outline">
              Login
            </Button>
            <Button className="w-full">Sign Up</Button>
          </div>
        </div>
      </div>
    </nav>
  );
};

Supabase配置文件(@/lib/supabase.ts)

import { createClient } from "@supabase/supabase-js";

const supabaseUrl = import.meta.env.SUPABASE_URL;
const supabaseAnonKey = import.meta.env.SUPABASE_ANON_KEY;

if (!supabaseUrl || !supabaseAnonKey) {
  throw new Error("Missing Supabase URL or anon key");
}

export const supabase = createClient(supabaseUrl, supabaseAnonKey);

解决建议

1. 修复Supabase环境变量读取问题

  • Astro中客户端组件无法读取不带PUBLIC_前缀的环境变量,需修改.env文件中的变量名:
    PUBLIC_SUPABASE_URL=你的Supabase地址
    PUBLIC_SUPABASE_ANON_KEY=你的Supabase匿名密钥
    
  • 同步更新supabase.ts中的变量引用:
    const supabaseUrl = import.meta.env.PUBLIC_SUPABASE_URL;
    const supabaseAnonKey = import.meta.env.PUBLIC_SUPABASE_ANON_KEY;
    
  • 重启Astro开发服务器,确保环境变量生效。

2. 修复移动端菜单无响应问题

  • 直接在组件顶层调用checkUser()会导致组件重复渲染,且可能在hydration阶段出错,影响交互逻辑。将checkUser()放入useEffect中:
    useEffect(() => {
      const fetchSession = async () => {
        // 解构session数据,避免判断逻辑错误
        const { data: { session } } = await supabase.auth.getSession();
        setLoggedIn(!!session);
      };
      fetchSession();
    
      // 可选:监听Auth状态变化,实时更新登录状态
      const { data: listener } = supabase.auth.onAuthStateChange((_event, session) => {
        setLoggedIn(!!session);
      });
    
      // 组件卸载时取消监听
      return () => listener?.unsubscribe();
    }, []);
    
  • 删除组件顶层的checkUser()调用。

3. 确保组件正确hydrate

  • 在Astro页面中使用Navbar组件时,必须添加客户端指令(如client:load),确保组件在客户端被激活:
    <Navbar client:load />
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 11:52:43