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

声明'use client'的React组件中window对象无法访问问题排查

问题:Next.js客户端组件中出现window未定义错误

我有一个显式声明了use client的客户端React组件:

"use client"

import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import * as z from "zod"
import { useState, useTransition } from "react"
import { useSearchParams } from "next/navigation"
import { LoginSchema } from "@/schemas"
import login from "@/app/actions/login"

export default function LoginForm() {
  const currentURL = `/${window.location.href.split("/").slice(3).join("/")}`
  const searchParams = useSearchParams()
  const callbackUrl = searchParams.get("callback") || currentURL

  const [isPending, startTransition] = useTransition()

  const form = useForm<z.infer<typeof LoginSchema>>({
    resolver: zodResolver(LoginSchema),
    defaultValues: {
      email: "",
      password: "",
    },
  })

  const onSubmit = (values: z.infer<typeof LoginSchema>) => {
    startTransition(() => {
      login(values, callbackUrl)
        .then((data) => {
          if (data?.error) {
            form.reset()
            setError(data.error)
          }
          if (data?.success) {
            form.reset()
            setSuccess(data.success)
          }
          if (data?.twoFactor) {
            setShowTwoFactor(true)
          }
        })
        .catch(() => setError("Something went wrong"))
    })
  }

  return (
    <form className="space-y-6" onSubmit={form.handleSubmit(onSubmit)}>
      ...
    </form>
  );
}

当我尝试通过window.location.href获取URL时,出现如下错误:

ReferenceError: window is not defined at LoginForm (./src/components/auth/LoginForm.tsx:40:28)

我原本认为window对象仅在客户端可用,所以这样写应该没问题,请问我哪里出错了?

我已尝试的操作
我在函数内添加了console.log()来打印currentURL的计算值:

const currentURL = `/${window.location.href.split("/").slice(3).join("/")}`
console.log(currentURL)

这能正常输出结果,说明组件显然可以访问window对象,但服务器仍抛出上述错误。我甚至不理解为什么服务器会报错,毕竟这个组件是在客户端渲染的!


原因与解决方案

问题根源

即使加了"use client",Next.js在服务器端渲染(SSR)或静态生成(SSG)阶段,还是会对客户端组件做初步的服务器端解析/渲染——这段代码会在服务器环境执行,而服务器没有浏览器的window对象,所以直接访问window会报错。你看到console.log正常输出是因为客户端运行时没问题,但服务器预渲染时已经触发了错误。

解决方法

1. 用useEffect延迟访问window(最稳妥的基础方案)

useEffect的回调只会在客户端组件挂载后执行,完全避开服务器端执行阶段:

"use client"
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import * as z from "zod"
import { useState, useTransition, useEffect } from "react"
import { useSearchParams } from "next/navigation"
import { LoginSchema } from "@/schemas"
import login from "@/app/actions/login"

export default function LoginForm() {
  const [currentURL, setCurrentURL] = useState("")
  const searchParams = useSearchParams()

  useEffect(() => {
    // 仅在客户端执行
    const url = `/${window.location.href.split("/").slice(3).join("/")}`
    setCurrentURL(url)
  }, [])

  const callbackUrl = searchParams.get("callback") || currentURL

  // ... 其他代码保持不变
}

2. 直接检查window是否存在(简单场景快速修复)

在访问window前先判断环境,给服务器端一个默认值:

const currentURL = typeof window !== "undefined" 
  ? `/${window.location.href.split("/").slice(3).join("/")}`
  : ""; // 服务器端时用空值或其他默认路径

3. 使用Next.js官方钩子usePathname(最推荐的最佳实践)

Next.js的next/navigation提供了usePathname钩子,专门用于获取当前路径,无需依赖window,且在服务器端和客户端都能正常工作:

"use client"
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import * as z from "zod"
import { useState, useTransition } from "react"
import { useSearchParams, usePathname } from "next/navigation" // 新增usePathname
import { LoginSchema } from "@/schemas"
import login from "@/app/actions/login"

export default function LoginForm() {
  const pathname = usePathname() // 获取当前路径
  const searchParams = useSearchParams()
  const callbackUrl = searchParams.get("callback") || pathname // 直接替换成pathname

  // ... 其他代码保持不变
}

这个方法完全符合Next.js的设计逻辑,避免了直接操作浏览器API带来的环境兼容问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 20:01:15