-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SSR + useSession causes an extra network call and render cycle #844
Comments
Did you ever find the fix for this or does it absolutely require a fix on next-auth side? |
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 The naive approach looks like this in import { getSession, Session } from "next-auth/client";
type ExpandedAppProps = AppProps & { session: Session };
const App = ({ Component, pageProps, session }: ExpandedAppProps) => {
return (
<MyCustomContext.Provider value={{ session }}>
<Component {...pageProps} />
</MyCustomContext.Provider>
);
};
App.getInitialProps = async ({ ctx }: AppContext) => {
const session = await getSession(ctx);
return { session };
}; The problem with this is that there's no way to access the session from What does this mean? It means this is a perfect place to use a In some common file import { Session, getSession } from "next-auth/client";
const sessionCache = new WeakMap<NextApiRequest, Session | null>()
export async function getCachedSession({ req }: { req: NextApiRequest }): Session | null {
const session = sessionCache.get(req)
if (sessionCache.has(req)) {
return sessionCache.get(req)!
} else {
const session = await getSession({ req })
sessionCache.set(req, session)
return session
}
} then you can call 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 |
Hi there! A reproduction repo with a link would be extremely helpful to debug your issue. 🙂 In your Then in your If you could verify the behavior you are describing with a minimal reproduction, then maybe it is something in 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. |
So I found something, which might actually be a bug on our end: Line 221 in cec46b0
The Also, there is something weird in the Line 145 in cec46b0
So this might be some kind of a leftover, I think. To check out the |
The file |
thanks, I'll keep digging then |
Hi there! It looks like this issue hasn't had any activity for a while. To keep things tidy, I am going to close this issue for now. If you think your issue is still relevant, just leave a comment and I will reopen it. (Read more at #912) Thanks! |
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 to other libraries/frameworks, a new file per framework could be added. We might also want to update the import from `next-auth/client` to `next-auth/react`. BREAKING CHANGE: Some performance issues (#844) could only be fixed by moving setting up event listeners inside `Provider`. This breaks the current tab/window sync behavior for users who don't use `Provider` in their app. For these users, wrap your app in `<Provider>` at a level above all of your `useSession` calls. If you don't care about tab/window syncing, this might not be needed, but still recommended.
**What**: 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 to other libraries/frameworks, a new file per framework could be added. **Why**: Some performance issues (#844) could only be fixed by moving more of the client code into the `Provider`. **How**: Refactoring `next-auth/client` Related: #1461, #1084, #1462 BREAKING CHANGE: **1.** `next-auth/client` is renamed to `next-auth/react`. **2.** In the past, we exposed most of the functions with different names for convenience. To simplify our source code, the new React specific client code exports only the following functions, listed with the necessary changes: - `setOptions`: Not exposed anymore, use `SessionProvider` props - `options`: Not exposed anymore, use `SessionProvider` props - `session`: Rename to `getSession` - `providers`: Rename to `getProviders` - `csrfToken`: Rename to `getCsrfToken` - `signin`: Rename to `signIn` - `signout`: Rename to `signOut` - `Provider`: Rename to `SessionProvider` **3.** `Provider` changes. - `Provider` is renamed to `SessionProvider` - The `options` prop is now flattened as the props of `SessionProvider`. - `clientMaxAge` has been renamed to `staleTime`. - `keepAlive` has been renamed to `refetchInterval`. An example of the changes: ```diff - <Provider options={{clientMaxAge: 0, keepAlive: 0}}>{children}</Provider> + <SessionProvider staleTime={0} refetchInterval={0}>{children}</SessionProvider> ``` **4.** It is now **required** to wrap the part of your application that uses `useSession` into a `SessionProvider`. Usually, the best place for this is in your `pages/_app.jsx` file: ```jsx import { SessionProvider } from "next-auth/react" export default function App({ Component, pageProps: { session, ...pageProps } }) { return ( // `session` comes from `getServerSideProps` or `getInitialProps`. // Avoids flickering/session loading on first load. <SessionProvider session={session}> <Component {...pageProps} /> </SessionProvider> ) } ```
Hi there! I am looking forward to getting rid of these requests, I hope this is clear. |
@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...
👉 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 I especially question the design decision to have a |
@balazsorban44 would be really helpful to see your opinion on the above. Next-Auth is an amazing project but I wish it positioned itself as primarily a BE library, with maybe few low-level FE helpers (while it stays framework agnostic). Do you agree that higher-level FE tools, like the above 20 lines of code in the docs (trivial to readapt for any fetching/rendering soution) could replace |
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) |
Does the fact that this thread is closed mean that there are no plans to fix the provider or hook? Could it be opened again? |
Thanks, @ivan-kleshnin |
The reason is React 18 with strict mode enabled will render components twice in dev environment. |
Which doesn't help all that much in practice but surely wastes a lot of CPU ticks, reducing your laptop battery life 😨 |
is this still the case? Does it not cache the session information rather than calling /api/session each time? |
@balazsorban44 @ivan-kleshnin I was still experincing the issue. And this got resolved by creating a custom useMemo hook. It would be great to get your input on this,
|
**What**: 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 to other libraries/frameworks, a new file per framework could be added. **Why**: Some performance issues (nextauthjs#844) could only be fixed by moving more of the client code into the `Provider`. **How**: Refactoring `next-auth/client` Related: nextauthjs#1461, nextauthjs#1084, nextauthjs#1462 BREAKING CHANGE: **1.** `next-auth/client` is renamed to `next-auth/react`. **2.** In the past, we exposed most of the functions with different names for convenience. To simplify our source code, the new React specific client code exports only the following functions, listed with the necessary changes: - `setOptions`: Not exposed anymore, use `SessionProvider` props - `options`: Not exposed anymore, use `SessionProvider` props - `session`: Rename to `getSession` - `providers`: Rename to `getProviders` - `csrfToken`: Rename to `getCsrfToken` - `signin`: Rename to `signIn` - `signout`: Rename to `signOut` - `Provider`: Rename to `SessionProvider` **3.** `Provider` changes. - `Provider` is renamed to `SessionProvider` - The `options` prop is now flattened as the props of `SessionProvider`. - `clientMaxAge` has been renamed to `staleTime`. - `keepAlive` has been renamed to `refetchInterval`. An example of the changes: ```diff - <Provider options={{clientMaxAge: 0, keepAlive: 0}}>{children}</Provider> + <SessionProvider staleTime={0} refetchInterval={0}>{children}</SessionProvider> ``` **4.** It is now **required** to wrap the part of your application that uses `useSession` into a `SessionProvider`. Usually, the best place for this is in your `pages/_app.jsx` file: ```jsx import { SessionProvider } from "next-auth/react" export default function App({ Component, pageProps: { session, ...pageProps } }) { return ( // `session` comes from `getServerSideProps` or `getInitialProps`. // Avoids flickering/session loading on first load. <SessionProvider session={session}> <Component {...pageProps} /> </SessionProvider> ) } ```
Describe the bug
If you use
getSession
to return asession
prop fromgetServerSideProps
and then useuseSession
in the component, theuseSession
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:
getServerSideProps
, which callsgetSession
and returns the session inpageProps.session
which is passed into the context provider in _app.tsxuseSession
returns[session, false]
since it is able to retrieve the session object from the Provider contextuseSession
again returns[session, false]
since it is able to retrieve the session object from the Provider contextI 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:
[session, true]
(wheresession
is the full valid session object)/session
[session, true]
(the same as the first render)[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
Thanks so much for all your work on this project! Overall it's been a joy to use.
The text was updated successfully, but these errors were encountered: