7

I am fetching (using Nextjs 13's fetch with {cache:'force-cache'}) from an api that returns a random joke. I noticed that fetch is being called twice during build.

Here's my code:

// page.js
import {RefreshButton} from './RefreshButton'

async function getRandomJoke(){
  const res = await fetch("https://v2.jokeapi.dev/joke/Programming?type=single", {cache:'force-cache'})
  const data = await res.json()
  console.log("fetch called. Joke is: ", data['joke'])

  return data['joke']
}

export default async function Home() {
  const joke = await getRandomJoke()

  return (
    <div>
      {joke}
      <RefreshButton/>
    </div>
  )
}

and here's the build log:

[=   ] info  - Generating static pages (2/3)fetch called. Joke is:  A programmer puts two glasses on his bedside table before going to sleep. A full one, in case he gets thirsty, and an empty one, in case he doesn't.

[==  ] info  - Generating static pages (2/3)fetch called. Joke is:  The generation of random numbers is too important to be left to chance.

When the page is first rendered, it shows the first joke. Upon clicking on <RefreshButton />, which is a client component that calls router.refresh() on click, the second joke is shown.

My questions are:

  1. why is fetch called twice during build?
  2. why would its data change upon refresh if it is statically generated?
juliomalves
  • 42,130
  • 20
  • 150
  • 146
jalen_blue
  • 165
  • 1
  • 2
  • 12
  • Does this answer your question? [Why is my React component is rendering twice?](https://stackoverflow.com/questions/48846289/why-is-my-react-component-is-rendering-twice) – possum Nov 18 '22 at 14:54
  • @possum No. Same observation after turning strict mode off. – jalen_blue Nov 18 '22 at 15:16
  • Not sure about the build-time double fetching, but `router.refresh` invalidates the cache, thus requesting new, fresh data from the server. – ivanatias Nov 21 '22 at 19:22
  • 2
    @ivanatias Thanks. But since the page is statically generated, shouldn't its fetched data be embedded in its html and not change anymore? Also, in production mode, router.refresh only invalidates the cache once. New data isn't fetched from the 2nd click onwards. Any idea if this is expected? – jalen_blue Nov 22 '22 at 01:17
  • 1
    Yes, you're right. Will investigate more about this and come back to you if I find something relevant. – ivanatias Nov 30 '22 at 23:24
  • 1
    I am having the same issue, I can't understand why my API is being called twice on build if the request is supposed to be cached. Any news on this? Thank you. – Soumynon Jan 20 '23 at 01:31
  • @Soumynon Unfortunately no. I have since reverted back to Next12. – jalen_blue Jan 20 '23 at 06:15
  • 1
    Yeah man the same thing is happening to me. I have tried everything I know of but no luck. – RiskItAll Feb 03 '23 at 11:29
  • I have the same issue. haven't found any solution yet? in production after reload page apis will fetch twice and I tried a lot but it didn't work but when a page is ssr the problem won't occur, confusing ;( – Samira Aug 09 '23 at 09:30

2 Answers2

0

Try to use revalidate in the fetch calls, the duplicate stopped after this implementation here

https://beta.nextjs.org/docs/data-fetching/revalidating

Birtan
  • 1
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 02 '23 at 14:13
0

"next": "13.0.7", "react": "18.2.0"

I searched a lot finally I found this way and it worked for me very well. I found the idea from react.dev.

This happens because the server renders the HTML with some initial state, and then the client-side JavaScript takes over and initialises the Zustand store. If the initial state is different from the state that the client-side JavaScript initializes, it can cause inconsistencies in our application.

import { useEffect, useState } from "react"

const HydrationZustand = ({ children }) => {
  const [isHydrated, setIsHydrated] = useState(false)

  // Wait till Next.js rehydration completes
  useEffect(() => {

    setIsHydrated(true)
    if(isHydrated)
         //fetch you apis

  }, [isHydrated])

  return <>{isHydrated ? <div>{children}</div> : null}</>
}

export default HydrationZustand

For more information you can see medium

To wrap our application use this :

import "../scss/style.default.scss
import Layout from "../components/Layout"
import HydrationZustand from "./hydrationZustand"

function App({ Component, pageProps }) {
  return (
    <HydrationZustand>
      <Layout {...pageProps}>
        <Component {...pageProps} />
      </Layout>
    </HydrationZustand>
  )
}

export default App
Samira
  • 2,361
  • 1
  • 12
  • 21