How to stop Next.js from running client side code twice or hanging indefinitely when using the 'use client' directive?

2.5k Views Asked by At

The problem

I have a scenario that I must use the 'use client' directive because my project uses Chakra UI and their components can't render in the server.

When I try running this sample code:

// 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 receive the expected output in my terminal running the next development server:

I am running server side!

But when I add the 'use client' directive at the top of the file, I receive this duplicated message in my browser console:

I am running client side!   (2)

I understand that the 'use client' directive forces all the functions to be ran in the browser, but I can't understand why they run twice. This is a problem because in my real project the line that awaits the Promise makes a request to a back end and Next.js can't cache these requests, resulting in duplicated requests hitting the API.

What I tried

Multiple files

I tried to move the getData function to another file and ran the project:

// 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>
  )
}

And, for my surprise, I saw this in my next development server

I am running server side!

and this in my browser console

I am running client side!   (2)

, making this hitting my API 3 times per page reload in my real application.

Experimental Server Actions

I also tried enabling 'Experimental Server Actions' in my next.config.js and added the 'use server' directive in app/otherFile.ts. When I try to reload the page, it hangs indefinitely and doesn't show any output.

Next steps?

Why does this happen and how can I avoid this behaviour? Is this possible?

I'm using Next.js 13.4.4 and React 18.2.0.

2

There are 2 best solutions below

1
On BEST ANSWER

Although the double render might be inconvenient during development, it should not impact your application's functionality or performance when deployed.

The issue could be related to react 18. You can see it here: https://github.com/vercel/next.js/issues/35822

You can try enabling or disabling Strict Mode in your next.config.js file by setting the reactStrictMode option to true or false.

I hope this information will be helpful.

0
On

This might happen because Next.js runs your page twice, once on the server and once in the browser. To avoid duplicating API calls, we can try to use React's useEffect hook to ensure getData function is only called once. Here's an example:

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>
  )
}

Hope this helps! Feel free to reach out if you have more questions!