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

Next.js中返回页面导致服务端组件多次渲染的问题排查

问题:Next.js服务端组件在router.back后多次渲染并报错

我用Next.js和Supabase开发应用,部分页面结构如下:

const SomePage = async () => {
  const user = await supabase.auth.getUser()

  return (
    <Suspense fallback={<Loading />}>
      <SomeClientSidePage user={user} />
    </Suspense>
  );
}

在SomeClientSidePage组件内,某些操作会触发路由跳转到其他页面。初始加载页面时一切正常,但执行router.back操作后,服务端组件会出现多次渲染的情况。

我在SomePage中添加日志语句测试:

const SomePage = async () => {
  const user = await supabase.auth.getUser()

  console.log('here')
  return (
    <Suspense fallback={<Loading />}>
      <SomeClientSidePage user={user} />
    </Suspense>
  );
}

发现'here'被打印了20多次(初始加载时仅打印1次),当打印次数超过35-40次的阈值时,会抛出以下错误:

Uncaught Error: async/await is not yet supported in Client Components, only Server Components. This error is often caused by accidentally adding `'use client'` to a module that was originally written for the server.

移除const user = await supabase.auth.getUser()及async声明后,问题完全消失。想请教该问题的原因及解决方法?


原因分析

  • 服务端组件重复渲染触发框架误判:你的SomePage是服务端组件(无'use client'指令),但router.back触发页面回退时,Next.js的缓存机制出现异常,导致服务端组件被反复执行。当执行次数超过框架内部阈值时,渲染逻辑出错,误将服务端组件判定为客户端组件,进而抛出async/await不支持的错误——因为客户端组件不允许直接使用async/await语法。
  • Supabase异步调用的副作用:supabase.auth.getUser()是服务端异步请求,每次组件渲染都会执行一次。重复渲染会引发大量重复请求,进一步加剧渲染循环,最终触发框架的错误保护机制。

解决方法

方法1:缓存服务端用户请求结果

使用Next.js的unstable_cache缓存supabase.auth.getUser()的结果,避免每次渲染重复调用:

import { unstable_cache } from 'next/cache'

const getCachedUser = unstable_cache(
  async () => {
    const { data: { user } } = await supabase.auth.getUser()
    return user
  },
  ['user-data'], // 唯一缓存键
  { revalidate: 60 } // 每60秒重新验证缓存
)

const SomePage = async () => {
  const user = await getCachedUser()
  console.log('here')

  return (
    <Suspense fallback={<Loading />}>
      <SomeClientSidePage user={user} />
    </Suspense>
  );
}

方法2:将用户逻辑迁移到客户端组件

如果不需要在服务端获取用户信息,直接在带'use client'的SomeClientSidePage中获取用户:

// SomeClientSidePage.jsx
'use client'

import { useEffect, useState } from 'react'
import { supabase } from '@/lib/supabase'

export default function SomeClientSidePage() {
  const [user, setUser] = useState(null)

  useEffect(() => {
    const fetchUser = async () => {
      const { data: { user } } = await supabase.auth.getUser()
      setUser(user)
    }
    fetchUser()
  }, [])

  // 包含router.back的业务逻辑
  return <div>{user?.email}</div>
}

此时服务端页面可简化为:

const SomePage = () => {
  return (
    <Suspense fallback={<Loading />}>
      <SomeClientSidePage />
    </Suspense>
  );
}

方法3:使用Supabase Auth助手(推荐)

Next.js与Supabase集成时,官方推荐使用Auth助手获取用户,它会自动处理缓存,避免重复请求:

import { createServerComponentClient } from '@supabase/auth-helpers-nextjs'
import { cookies } from 'next/headers'

const SomePage = async () => {
  const supabase = createServerComponentClient({ cookies })
  const { data: { user } } = await supabase.auth.getUser()

  return (
    <Suspense fallback={<Loading />}>
      <SomeClientSidePage user={user} />
    </Suspense>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 17:57:03