next-auth: SSR + useSession causes an extra network call and render cycle
Describe the bug
If you use getSession to return a session prop from getServerSideProps and then use useSession in the component, the useSession hook will cause the page to re-render twice (three times total) even though it returns the valid session object all three times.
In addition, the hook will still cause the client to make a network request to the server after its first render, even though it already has the session object.
Steps to reproduce
You can observe this behavior by adding a console log to the render method of /server in next-auth-example and watching network requests that it makes.
Expected behavior
As far as I can tell, the expected behavior would go like this for a server-side rendered page:
- the incoming request causes nextjs to to call
getServerSideProps, which callsgetSessionand returns the session inpageProps.sessionwhich is passed into the context provider in _app.tsx - nextjs renders the page on the server, where
useSessionreturns[session, false]since it is able to retrieve the session object from the Provider context - the client hydrates and renders for the first time, and
useSessionagain returns[session, false]since it is able to retrieve the session object from the Provider context - no additional renders or network requests are triggered
I could definitely be mistaken about what’s expected - but my understanding from the Provider documentation is that there should not be an extra network request.
Screenshots or error logs
What instead happens is that the client renders three times:
- on the first render, useSession returns
[session, true](wheresessionis the full valid session object) - the client dispatches a network request to
/session - the client re-renders, useSession returns
[session, true](the same as the first render) - the network request resolves
- the client re-renders again, useSession returns
[session, false]
As another example, if you block network requests to /session, then the page first renders a logged-in view (once), and then a logged-out view (twice):
These were tested on both development and production builds, without un-focusing or re-focusing the window. I can’t think of anything else that could be causing the re-renders.
Additional context
I think that #487 originally encountered the same root problem, although their complaint was about the session callback (on the sever) getting triggered several times and the discussion ended up addressing ways to avoid using the session callback altogether.
Adding a session callback with a console log to the example will show that it is invoked twice on every /server page request - once during SSR and then again when the client makes its network request after rendering once.
Feedback
- Found the documentation helpful
- Found documentation but was incomplete
- Could not find relevant documentation
- Found the example project helpful
- Did not find the example project helpful
Thanks so much for all your work on this project! Overall it’s been a joy to use.
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 7
- Comments: 16 (4 by maintainers)
Commits related to this issue
- feat: set session to the context on a server side if using getServerSideProps #844 — committed to lnikell/next-auth by lnikell 3 years ago
- feat(client): move event listeners into Provider These changes ensure that we work more tightly with React that can also result in unforeseen performance boosts. In case we would decide on expanding ... — committed to nextauthjs/next-auth by balazsorban44 3 years ago
- feat(react): create client tailored to React (#1473) **What**: These changes ensure that we work more tightly with React that can also result in unforeseen performance boosts. In case we would dec... — committed to nextauthjs/next-auth by balazsorban44 3 years ago
Hi there! I am writing this comment while I am using version 4, in fact, I am not entirely sure if my problem is related to what was presented in this problem or that the fixes that were introduced solve my problem, but despite all this, I am facing a similar problem, I will describe it. I use the
useSessionhook in order to get the session and store it in context, it works fine except it sends many extra requests to the/api/auth/sessionwhich are useless after actually getting the session for the first time, I mean the session is checked repeatedly whenever lose focus and refocus the window. I attached a screenshot of the successive requests.I am looking forward to getting rid of these requests, I hope this is clear.
I think doing it “right” does require some kind of fix or re-structuring on the next-auth side, but I can share a workaround I’ve been using in the meantime.
The basic goal is to write our own context that provides the
sessionobject.The naive approach looks like this in
_app.tsx:The problem with this is that there’s no way to access the session from
getServerSideProps, sincegetServerSidePropsis only passed actx: GetServerSidePropsContextobject. I don’t think thisctxis always the same (===) one that is passed toApp.getInitialProps, but the request objectctx.reqdefinitely is the same for both.What does this mean? It means this is a perfect place to use a
WeakMap!In some common file
utils.ts:then you can call
await getCachedSession(ctx)from bothApp.getInitialPropsand fromgetServerSidePropsand it’ll only end up callinggetSessiononce. Then on the client, we can get the session withuseContext(MyCustomContext)whenever we need it.Hope this helps! It’s pretty hacky - it’d be really great if the next-auth provider could encapsulate this.
(you might have to mess around with the types a little bit since I think
ctx.reqis aNextApiRequestwhen it’s inApp.getInitialPropsbut just a plainIncomingMessagewhen it’s ingetServerSidePropsor something…)@thilllon
The reason is React 18 with strict mode enabled will render components twice in dev environment. https://beta.reactjs.org/learn/keeping-components-pure#detecting-impure-calculations-with-strict-mode
We are working on making the next-auth core fully framework agnostic. 👍
you’ll be able to implement your own client however you want, or just use the REST api directly (that you can already do, see https://next-auth.js.org/getting-started/rest-api)
@3li7u it’s a classic case of “The road to hell is paved with good intentions”. React and Next-Auth try to help us a bit too much…
useSessiondoes not deduplicate queries. Don’t useSessionProvideranduseSessionand write your ownuseSessionhook like https://github.com/nextauthjs/react-query/blob/main/index.js It can be based on React-Query or Apollo-Client or URQL or whatever you prefer.👉 You really want to fetch & cache all data with a single “state-management-and-fetcher” tool (of your choice):
To me, using an out-of-the-box
useSessionfeels like using an out-of-the-boxsigninform. It’s approapriate as a one-off, dev-mode solution but it’s not intended to be a final code.I especially question the design decision to have a
SessionProviderthat triggers the first fetch. In my mind no component named like*Provider*should ever fetch or do other effects.Hi there! A reproduction repo with a link would be extremely helpful to debug your issue. 🙂
In your
getServerSideProps, you should return thesessioninprops. Please see the Next.js docs.Then in your
_app, you should passpageProps.sessionto theProvider({ Provider } from 'next-auth/client')If you could verify the behavior you are describing with a minimal reproduction, then maybe it is something in
next-auth.UPDATE: sorry, I have to read through the OP’s comment a bit more thoroughly, you say it happens with our official example as well. I’ll have a look.