如何阻止Next.js在使用'use client'指令时两次运行客户端代码或无限期挂起?

vsnjm48y  于 2023-06-22  发布在  其他
关注(0)|答案(2)|浏览(107)

问题

我有一个场景,我必须使用'use client'指令,因为我的项目使用Chakra UI,它们的组件无法在服务器中呈现。
当我尝试运行以下示例代码时:

// app/page.tsx

export async function getData() {
  const side = typeof window === 'undefined' ? 'server' : 'client'
  console.log(`I am running ${side} side!`)
  const dados = await Promise.resolve([{ name: 'Hi' }, { name: 'Hello' }])
  return dados
}

export default async function Home() {
  const data = await getData()
  return (
    <ul>
      {data.map(({ name }: any) =>
        <li key={name}>{name}</li>
      )}
    </ul>
  )
}

我在运行下一个开发服务器的终端中收到预期的输出:

I am running server side!

但是,当我在文件顶部添加'use client'指令时,我在浏览器控制台中收到以下重复消息:

I am running client side!   (2)
  • 我知道'use client'指令强制所有函数在浏览器中运行 *,**但我不明白为什么它们会运行两次。**这是一个问题,因为在我的实际项目中,等待Promise的行向后端发出请求,而Next.js无法缓存这些请求,导致重复的请求击中API。

我所尝试的

多个文件

我尝试将getData函数移动到另一个文件中,并运行该项目:

// app/otherFile.ts

export async function getData() {
  const side = typeof window === 'undefined' ? 'server' : 'client'
  console.log(`I am running ${side} side!`)
  const dados = await Promise.resolve([{ name: 'Hi' }, { name: 'Hello' }])
  return dados
}
// app/page.tsx

import { getData } from './otherFile'

export default async function Home() {
  const data = await getData()
  return (
    <ul>
      {data.map(({ name }: any) =>
        <li key={name}>{name}</li>
      )}
    </ul>
  )
}

而且,令我惊讶的是,我在我的下一个开发服务器中看到了这个

I am running server side!
    • 和**这在我的浏览器控制台
I am running client side!   (2)

,使得在我的实际应用程序中,每次页面重新加载都会命中我的API 3次。

实验服务器动作

我还尝试在我的next.config.js中启用'Experimental Server Actions',并在app/otherFile.ts中添加了'use server'指令。当我尝试重新加载页面时,它会无限期挂起,并且不显示任何输出。

下一步?

为什么会发生这种情况,我如何避免这种行为?这可能吗?
我使用的是Next.js 13.4.4和React 18.2.0。

xmq68pz9

xmq68pz91#

尽管双重呈现在开发过程中可能不方便,但在部署时它不应影响应用程序的功能或性能。
问题可能与React18有关。你可以在这里看到:https://github.com/vercel/next.js/issues/35822
您可以通过将reactStrictMode选项设置为true或false来尝试在next.config.js文件中启用或禁用严格模式。
我希望这些信息会有帮助。

56lgkhnf

56lgkhnf2#

这可能是因为Next.js会运行页面两次,一次在服务器上,一次在浏览器中。为了避免重复API调用,我们可以尝试使用React的useEffect钩子来确保getData函数只被调用一次。下面是一个例子:

import { useEffect, useState } from 'react'
import { getData } from './otherFile'

export default function Home() {
const [data, setData] = useState([])

useEffect(() => {
  const fetchData = async () => {
  const response = await getData()
    setData(response)
}
fetchData()
}, [])

return (
 <ul>
   {data.map(({ name }) => <li key={name}>{name}</li>)}
 </ul>
  )
}

希望这有帮助!如果您有更多问题,请随时联系!

相关问题