vercel / next.js

The React Framework
https://nextjs.org
MIT License
126.83k stars 26.96k forks source link

Stale data after navigation when using internal API and SSR (app router) #69979

Closed sidletski closed 1 month ago

sidletski commented 1 month ago

Link to the code that reproduces this issue

https://github.com/sidletski/next-cache-issues

To Reproduce

  1. Clone the example project
  2. Install dependencies and run the development server (:3000 port)
  3. Navigate to the main page, which displays a list of users fetched from the internal API
  4. Click on the "Edit user" button for a specific user to go to the edit page
  5. Make changes to the user's information and submit the form
  6. Click the "Go back" button to return to the main page
  7. Observe that the user list on the main page is not updated with the new data
  8. Click the "Edit user" button again for the same user (or use browser back button)
  9. Notice that the edit form displays the old, unedited data
  10. Refresh the page manually (e.g., F5, Cmd+R or browser refresh button)
  11. Observe that only after a force reload, the updated data appears

Current vs. Expected behavior

After editing a user and navigating back using the "Go back" button (which uses next/link) or the browser's back button, the main page shows stale data, but I want the list to reflect the updated data unstable_ functions from the next/cache package doesn't help :(

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.4.0: Fri Mar 15 00:10:42 PDT 2024; root:xnu-10063.101.17~1/RELEASE_ARM64_T6000
  Available memory (MB): 16384
  Available CPU cores: 10
Binaries:
  Node: 18.17.0
  npm: 9.6.7
  Yarn: 1.22.19
  pnpm: 8.15.7
Relevant Packages:
  next: 14.2.9 // Latest available version is detected (14.2.9).
  eslint-config-next: 14.2.9
  react: 18.3.1
  react-dom: 18.3.1
  typescript: 5.6.2
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Navigation, Pages Router

Which stage(s) are affected? (Select all that apply)

next dev (local), next start (local), Vercel (Deployed), Other (Deployed)

Additional context

No response

abhi12299 commented 1 month ago

the repo you linked has nothing apart from the boilerplate code from create-next-app.

sidletski commented 1 month ago

the repo you linked has nothing apart from the boilerplate code from create-next-app.

That's true, sorry. It's updated now

abhi12299 commented 1 month ago

This is not an issue with nextjs. I see the following issues in your code:

  1. try to build the project and it will not work. the home page will try to prerender by fetching data from localhost:3000/... but there's nothing running on that port.

  2. you need to add export const dynamic = "force-dynamic"; to your page.tsx file disable prerendering

  3. when you build the project you will see this output. notice /api/users is prerendered as static content. you dont want that to happen. so have export const dynamic = "force-dynamic"; in your api route as well. the other api route is not static because it uses dynamic params.

    image
  4. the home page is a server component, and when you navigate on the client side (by going back from edit page), nextjs will not run the fetch again. so you need to have a separate userlist component as a client component, and fetch the data in a useEffect.

  5. if you do not have a client component, and instead stick to the server component, you'll notice that after editing and going back to the homepage, when you refresh, you still get the same old data. that is because of data cache, and you can opt out of it using the cache option. read more here: https://nextjs.org/docs/app/api-reference/functions/fetch

sidletski commented 1 month ago

@abhi12299

Thank you for looking into my issue! The main purpose of the api + db file was to simulate an API interacting with a database, and it works as intended in dev mode only. To make this clearer, I've updated the example project by using SQLite3 and made the /api/users route dynamic. In the updated version, I'm using both the unstable_noStore function and cache: 'no-store' for the fetch call, but the data still doesn't update when using the browser's back button. I suspect this happens because the new page isn't being requested, and only client-side navigation is occurring. Isn't there way to get actual data when having server components? I don’t recall having this issue with older Next.js versions that used getServerSideProps, but feel free to correct me if I'm wrong.

abhi12299 commented 1 month ago

I suspect this happens because the new page isn't being requested, and only client-side navigation is occurring. Isn't there way to get actual data when having server components?

The answer is in your question. When client side navigation occurs, the homepage is not rendered again - it is loaded from the client side router cache. Read about it here: https://nextjs.org/docs/app/building-your-application/caching#client-side-router-cache - it also shows how you can invalidate the cache by calling router.refresh, so maybe that helps with your usecase.

Also, from the docs, unstable_noStore can be used to declaratively opt out of static rendering and indicate a particular component should not be cached.

This means that you can mark the page as dynamic, but it won't server-render it again after client side navigation.

sidletski commented 1 month ago

you can invalidate the cache by calling router.refresh, so maybe that helps with your usecase.

Thank you, this approach helped! It's quite a good solution, considering that now when data changes, I can have the same effect as in the previous Next.js flow using getServerSideProps, as shown here: https://github.com/sidletski/next-cache-issues-ssr/tree/main/src Is it a good practice to run router.refresh function when submitting any info in the form? Or sometimes maybe even on every page that could be highly dynamic?

abhi12299 commented 1 month ago

Is it a good practice to run router.refresh function when submitting any info in the form? Or sometimes maybe even on every page that could be highly dynamic?

Just use client components in that case - no need to get the RSC payload from the server, when you only have to fetch the data dynamically.

sidletski commented 1 month ago

@abhi12299 Thank you! Would be also nice to have all the new features like a separate loading layout and fetching using server components, but CSR will work for me, too.

github-actions[bot] commented 1 month ago

This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.