Using SessionProvider with SSR
I encounter an error whenever the remix helpers are imported. It looks to be a build time error because this error happens even when the code is not executed.
58 Replies
Is there a Remix tutorial or starter app you're using, so I can repro exactly what you've got?
or is this an existing remix app you're adding Convex to?
https://github.com/conradkoh/convex-remix-error
I created a reproduction here!
GitHub
GitHub - conradkoh/convex-remix-error
Contribute to conradkoh/convex-remix-error development by creating an account on GitHub.
you can refer to the file app/routes/app.tsx
thanks for the help looking into this @ballingt
I think this is an issue with the way that convex-helpers is packaged.
Nice repro, helpful to see that that code didn't have to run
@conradkoh try 0.1.28-alpha.1 for convex-helpers
that fixes for me
ah you knew convex-helpers were the problem the whole time, just typo in your question s/remix helpers/convex-helpers/ I think
I think the change will be released before long, it's just a significant change so @ian has been vetting it
@conradkoh let me know how you find Remix, I'd like to add a quickstart for it in our docs and I'm not sure what else we need to do to set it up. It might be that some server-side helpers would be useful.
Just published 0.1.28!
oh, my bad! I didn't realise the typo.
mm right! will let you guys know. I actually was building some server side session auth in Next 14. but was finding that I kept having to use 'use client' everywhere with the new server centric paradim on all the react frameworks. had a friend recommend remix and thought I'd sieze the change to learn remix and see how far I can take it.
thanks @ian @ballingt, you guys are insanely fast.
by the way @ian @ballingt , can confirm that the original issue has been resolved.
there was one more issue, that is kinda related to sessions and the state of docs, but also not likely to be the same cause. this has to do with a stack post written by @ian here - https://stack.convex.dev/track-sessions-without-cookies
the issue is that the examples in the post do not work as expected and are a little more involved.
the example of what is broken, and also the fix can be found here: https://github.com/conradkoh/convex-remix-error/commit/0ef7ca31999ad661411093fea1172752e7a6190a
Track Sessions Without Cookies
Advice and resources for session tracking per-tab or per-browser via localStorage / sessionStorage using React Context, hooks, and some utilities to m...
Ah interesting - I'll have to dig into that. A couple things I noticed:
1. The version in the page of usehooks-ts is 2.9.5, whereas this version is ^3.1 . I bet the API for the hook changed. I just updated the helper types to accommodate it (
convex-helpers@0.1.29-alpha.1
).
2. The uuid generation is using crypto
which isn't available server-side. To get around this, you can use the uuid
package like this:
3. The biggest issue is that sessionIds that are stored in localStorage really don't play well with SSR. The server render doesn't know what the session ID is, and generating a new one on the server causes hydration issues. The server could theoretically create one and pass it down, but the client might already have one. I think a solution here is to use a useStorage
that stores the value in a cookie - read on the server, and if it doesn't exist, it writes it there. then the client also reads the value, and refreshing on the client resets the cookie for the next request. Moving to cookies loses some of the CSRF protection of localStorage, however.
I would recommend not using sessions server-side for now- mount the provider on the client, and any component that needs the session ID should also be a client component. Open to more thoughts on this. I haven't thought deeply about server-rendered sessions yetmm yeah precisely! I still really wanna keep my client centric model with local storage tbh (I personally feel this is how convex is also designed to work perhaps?). the server centric model is really less appealing.
so I feel the default behavior should be
1. if called on server, no-op
2. if called on client - do something
this way, it is always "safe" to render on the server. the client will catch up eventually after the first server centric render? wdyt?
personally, with a framework like convex (especially with the whole realtime first paradigm), server side render as a performance optimisation would be the last thing on my mind. if data is fetched primarily by the client over a websocket, would I really expect my server to do the same? and if not, would I write two sets of data fetching logic? I doubt.
so I think the compatibility with server renders shouldn't go too far - I think it should just not crash and that'll be good enough!
As long as you can figure out the hydration issues - if you no-op on the server then make it on the client then hydration will complain - not sure what the normal approach there is for remix
I'm not sure what the behavior of the hook should be during server renders - whether it should refuse to render its children e.g. or maybe it should no-op any attempts to query with a session ID. I was hoping something like
idGenerator={ typeof window === "undefined" ? () => "" : undefined }
would fix it, but that causes hydration issues I thinkmm, hydration issues meaning that it will complain that the initial render on the server has different content when rendered on the client?
what about perhaps wrapping the component in a
useEffect
(which doesn't run on the server anyways) and then execute it? since the effect only runs on the client, both the initial render on the server and on the client should have the same behavior, and then the effect will run to cause it to load.
will find some time to give it a try, and share what works if I find a solution!hey @ian, I added an example that seems to work for remix here. I'm not doing anything crazy so hopefully this works for Next too, without using any directives etc.
- https://github.com/conradkoh/convex-remix-error/commit/e7d5003e786baa0c99172df8051155a66fc6fa0f
I think this should have no hydration errors, and also enforce that all children of the session provider will only render if the session provider has a ready session id (only on client).
Cool - that gives me another idea:
1. The id defaults to "" and sets the ID with a setEffect (like your PR)
2. the useSessionQuery / etc. skips the query when the id is ""
That way the whole UI can be SSR-ed, it just won't have any query results on the server (which is the norm anyways). Some non-session SSR requests could be made using the usePreloadedQuery hook (you should check that out - pretty nifty for hybrid server/client rendering). With your current proposal, it wouldn't render any of the UI under SessionProvider - which is usually your whole app
yeah, I think your idea is a better approach for implementation in the library.
I think the challenge here will be how the children should behave, if the session ID is not available yet. I wonder how people are currently using it.
- when I was using nextjs for this, I basically just put the 'use client' directive everywhere until I stopped getting errors.
- in remix (since they don't have support for the 'use client' directive), I had to find alternatives. I ended up using
lazy(...)
everywhere to try to work around it until I couldn't
essentially, the library now fails during the initial render on the server, so it might be good to just not fail with the useSessionId()
and SessionProvider
combo, so that anyone who wants to do server rendering can still do it.
maybe undefined
as a return value may be more in line with conventions from the useQuery
hook to represent "loading" states?
regarding this:
the useSessionQuery / etc. skips the query when the id is ""how would you prevent hydration errors since the the id is not on the server though? if the initial render on the server has ID = "", then the initial render on the client also has to be "" for consistency I think. And if not, then the initial render will still need to be empty string, followed by subsequent loaded values - which would seem to converge on a
useEffect
kind of approach.
I think the main question is more about whether children should render before session id is available, or after. There would be 2 cases:
1. if session id is used for unauthenticated sessions, then children should still render content on the server.
2. if session id is used for authentication, then children should only render after session id is available. this allows all child components assume a client runtime (instead of writing code to handle both cases, and we can safely access localStorage). as long as auth is required, we are always going to be in the client boundary (aka wait for session id) before anything can happen.
sorry for the multiple edits.Yes I think it'd render as "" (or undefined) on the first pass on both the server & client. On the client, I'm not sure about (2). In that case I think they should just use framework-specific tools to avoid rendering on the client. It might be useful to render the skeleton of all the page even before it loads user-specific data. And I can't think of a risk of having a "" session ID for one pass - it's mostly used to pass to server functions, and if those functions are skipped when it's "", then I think we're good. Using sessionID for auth doesn't mean having a sessionID means you're logged in, so the client can't know if it's authenticated merely by the existence of it
mm it makes sense I think. and yeah I think there can be solutions that are done in the framework / user code too.
with that said, it might be helpful to users to have a clear mental model for authentications, and implications of where they choose they store their session id. if we evaluate the logical conclusions of choosing storing session on local storage
1. when content does not require auth, we can render children on server (similar to what was suggested)
2. when content requires auth, => auth requires session id => component / hook must be run in client
the second chain of reasoning is more hidden, and will result in a fair amount of trail and error, and so maybe some auth helpers that take care of this with a
<AuthenticatedBoundary sessionQuery={api.query.session}>{children}</AuthenticatedBoundary>
might be helpful?
from how I'm using convex react helpers - I see it as mostly a client centric set of tools.
since convex is all about web sockets and realtime updates across clients, I do not really expect much support for server side rendering at this point, since there are implications of choosing to fetch data one time over web sockets on the server anyways.
perhaps you could share more if this is a good way to think about it or if there's perspective I'm missing?GitHub
use ssrFriendly by ianmacartney · Pull Request #1 · conradkoh/conve...
see how this works for you
The flow for an authenticated component, without SSR data fetching, is:
1. The server renders the UI with a dummy sessionID, not fetching any data since useQuery only runs client-side anyways.
2. The client rehydrates the UI with a dummy sessionID, and any useSessionQuery does not load (it uses the "skip" parameter).
3. The client runs a useEffect revealing the actual sessionID, and any useSessionQuery then updates and runs with the sessionID.
Make sense?
I cut
convex-helpers@0.1.29-alpha.2
with a ssrFriendly
flag you can pass the session provider, so if you're not doing SSR you won't have the SSR_DEFAULT value returned on the first pass.
But I don't know remix enough to know if this is sufficient. I'd love to know if it works how you'd expect before I release anythingthanks @ian . what I'm seeing is that there are some hydration errors as a result of the dummy session ID.
interesting - the only way the client would render that the first time is if it runs a useEffect before calculating what to hydrate.
i'm not well versed at SSR workarounds so maybe it's something obvious: https://github.com/get-convex/convex-helpers/blob/sessions/packages/convex-helpers/react/sessions.ts#L122
GitHub
convex-helpers/packages/convex-helpers/react/sessions.ts at session...
A collection of useful code to complement the official packages. - get-convex/convex-helpers
I am getting something super strange. so I'm trying to debug what is going on by going to the lib code and modifying the
node_modules/.pnpm/convex-helpers@0.1.29-alpha.2_convex@1.11.0_react@18.2.0/node_modules/convex-helpers/dist/react/sessions.js
directly.
I modified the code
and somehow it seems to still render fine on the client?
I should think that the whole session context should be missing and no content should render. 🤔
I'm not sure if there is some bundling issue or module resolution issue going on. also to note, it renders fine on the client, but any changes I make to this file are actually being updated on the server itself - so console logs etc work fine.
I think there was some weird cache issue that a restart didn't help. but somehow deleting my node_modules fixed it 🤯
sorry for the trouble, but I think everything seems to be working correctly now.Working with the above changes to helpers? Or out of the box? Btw you can clone helpers locally and import it as a relative path to get live updates. Just run npm run watch in the packages/convex-helpers directory
@conradkoh did it work without the
return (React.createElement("div", null));
?hey @ian, sorry i didn’t get to this sooner. the hydration issue in
convex-helpers@0.1.29-alpha.2
was resolved by deleting node modules. so I believe that it works.I'm still seeing some things:
Extra attributes from the server: data-new-gr-c-s-check-loaded,data-gr-ext-installed
but that might be unrelated?
Ah it went away when I blew away .cache. And if I force-reload a ton of times it can happen, but maybe that's just a race / bug with remix/vite ?
Shipped it as 0.1.29 just nowi'm curious - which .cache folder did you kill? (cos I've been trying to clear any cache I can to resolve weird issues with remix). I only found the folder
node_modules/.vite
.@ballingt @ian btw - I hope this helps. I put together a new repo, with step by step commits showing what it takes to get from a new remix app to a basic working state with convex.
https://github.com/conradkoh/remix-convex-quickstart/commits/master/
one of the frustrating things personally is that I don't know the step by step before and after state. maybe I'm overcompensating but decided to commit after each modification so that it would mirror most closely what you may want to reproduce on your own and ultimately include in your docs.
in the cases where the command is run in the terminal, I added the command I ran into the commit message.
GitHub
Commits · conradkoh/remix-convex-quickstart
Remix Convex Quickstart. Contribute to conradkoh/remix-convex-quickstart development by creating an account on GitHub.
btw I missed out - the command used to create the project is
pnpx create-remix@latest
. will update the main readme.
ok done.Great work @conradkoh, thanks for doing this!
@ian I just double checked - I think things look good! the initial issue experienced is gone! thanks for the quick work to look into and resolve this.
one last thing - I'm seeing that the session id returned is empty string. do you think undefined in this case would be better, if the goal is to indicate a loading state? otherwise if '' was stored in the local storage it would be indistinguishable from the actual loading state as well.
thanks @ballingt, glad it helped!
On unfortunate thing about localStorage / sessionStorage is that they don't support undefined - they encode everything to strings, turning it into
undefined
which can't JSON parse. I wish setting it to undefined would just delete the entry but alas
Though I just read the code and the usehooks-ts version special-cases returning undefined so maybe that's the move, and I can update my useSessionStorage implementation to also support it. I would still return the default, but at least if the browser crashes before the useEffect renders they won't have a bad ID storedNot sure if I'm understanding correctly. I think it is actually good that local and session storage do not support undefined, as this removes any ambiguity as to whether the state is loading or not. also,
localStorage.getItem("xyz")
returns null
if the value is not set.
A naive mapping in my head is this:
- during loading => sessionId = undefined
- after loading
1) if sessionID null => generate new ID
2) if sessionID was present => return previously generatedID
On the server, the session ID should always be undefined. Outside of the lib, the user has to choose how they want to handle if the session ID is undefined. Either
a) render children (usually if auth is not required - which also means can be rendered on server and hydrated)
b) show a loading state and wait for session id (if an auth check is required)
Does this make sense or is there something I'm missing?
Also, wanted to find out more about why the docs would require an installation of something like usehooks-ts
. Was wondering if it makes sense to simplify re-export the useLocalStorage
hook from convex-helpers
itself.
More precisely speaking, was wondering if we could remove usehooks-ts
as a dependency of the user's app and make it in convex-helpers
instead.
This would
1. ensure that the parity between the usehooks-ts
return value and the input interface of <SessionProvider useStorage={...} />
is maintained and version pinned
2. remove weirdness if user already has another utility library like react-use
or @uidotdev/usehooks
There’s a few libraries that provide something like useLocalStorage. I want folks to just use whatever they’re already using. If you don’t provide anything it’ll use the internal useSessionStorage as a default (tab-specific). I don’t want to pull in a dependency that’s not required unless necessary. Most folks don’t use sessions I think. There’s probably a smart way to keep this contract without an explicit dependency though
I could make a version of the hook that is SSR-specific (also maybe more friendly with async stores) that sometimes returns undefined, but for the non-SSR world the mental model is much easier when you can always assume it’s present. Eg would useSessionMutation throw when it’s called? Queries can suspend which is nice.
I used to have a helper that forced the server to always handle nulls and the DX wasn’t great
@conradkoh I changed the default to undefined (and to not store for useSessionStorage) in version 0.1.33. It still returns the dummy value, but it won't save that to localStorage, and will recover more gracefully if the value in localStorage was ever stored as undefined / dummy string
mm that makes sense - I didn’t factor in the async storage use case.
sounds good! i’ll give it a try when i get a chance, thanks for working on this!
@ian I was trying the latest version and the SSR value is returned from the
useSessionId
hook. given this spec, do you have any recommendation for now a user might determine if the useSessionId value is safe to use?if encoding the value as
undefined
from useSessionId
is too hairy (maybe I'm still missing something), wdyt of just returning an isReady
property from the hook, where isReady = sessionId != 'SSR default session ID'
Right now you can use
!== SSR_DEFAULT
to get this. I'm curious what you're using the sessionId for directly? I have just used the useSessionQuery
/ useSessionMutation
usually
I can make useSessionMutation/Action
throw if the value isn't set (shouldn't happen unless maybe there's a useEffect immediately firing one off?)
And yeah since useSessionId
is not commonly used directly, I suppose making the return type string | undefined
then wouldn't really do much harm. But I'd rather that only be the type for the SSR version, since it'll always be defined otherwise. and the useSessionId
function isn't defined within the scope of whether ssrFriendly
is true/false, so the types are static
Yeah I'm coming around on useSessionId
just returning | undefined
, especially with a new parameter like useSessionId("ssr")
. I put it up with version 0.1.34-alpha.3
- you can install with convex-helpers@alpha
.
This is a breaking change in that I no longer check for SSR_DEFAULT, but I don't think anyone's been using the SSR flow except you since we've been jamming on it.
Does that one work for you? The useSessionId("ssr")
will have types SessionID | undefined
and if you don't pass "ssr"
it will throw if you're using it in an ssrFriendly
context or if the session ID is undefined.mm I'm using the session ID mainly to pass i back into queries and mutations. there's probably another helper somewhere but I haven't gone to try looking for it.
my view of the whole auth model is just that I won't bother rendering any auth-ed components until session is safe to access on the client anyway - so I think it's fine for me.
I think
ssrFriendly
doesn't have a clear meaning,. it could be friendly in that children are rendered on the server and thus not blocked, or friendly in that it will never render on the server and thus session ID will always be "correct".
and useSessionId("ssr")
seems weird, because hooks generally render in both the server and the client. so the expectation should just be for useSessionId()
to work regardless.
as a user, if something in the framework breaks cos of rendering on the server
- in next.js I'll probably just use a 'use client' directive
- in remix, I probably have to force client side rendering by hiding it with useEffect
I don't think I'll really try to add the ssr
param nor the ssrFriendly
param into the context unless I really know what it does.
if people don't use useSessionId
directly, how should they usually use it? I was looking back at the stack post - is useSessionQuery()
the expected way to call it?
maybe my complaints are unfounded, I could probably follow through better with the stack post.Yeah if you useSessionQuery or useSessionMutation you never need to handle the session id directly
And if you’re loading data with useSessionQuery then it will not pass the session Id on the first pass if it’s ssrFriendly
I’ll think on naming. ssrFriendly is in a way : defer session queries until after the first pass. The useSessionId param should at least be consistent or i can just have it always returns undefined since it’s usually less useful
@conradkoh I'm coming around on simplifying it so that:
- useSessionQuery will wait to make a query until the sessionId is set (already the case)
- useSessionMutation & useSessionAction will await the sessionId if it isn't set yet, so they won't throw from a useEffect but just delay until the sessionId is made (should be very fast)
- useSessionId will just return string | undefined always, with no extra parameter.
So you'd just set
ssrFriendly
to render all the children but not act on the sessionId until it's set (which will only happen on the client) and if you use useSessionId
directly, you always have to account for it being undefined
. But it will also return the promise to await if it's undefined
. So you can duplicate the useSessionMutation
behavior yourself if you'd rather.
Before I make this change, are there any other pieces of feedback I should consider incorporating? I really appreciate your help so far in developing the API. Want me to mention you in the GitHub PR?convex-helpers@0.1.34-alpha.4
has the new behavior, in this PR: https://github.com/get-convex/convex-helpers/pull/117
I haven't tested enough to release it, but I'm happy with where it is nowGitHub
sessionIdPromise instead of throwing by ianmacartney · Pull Request...
Changes behavior for SSR:
The value will not be persisted until there's a valid ID.
Queries will skip until there's a valid ID
Mutations and actions will await a promise for the first vali...
thanks for this
regarding
ssrFriendly
, may I know what the spec is? from what you shared, it sounds like the children will be rendered, even in the session ID is not present. a few clarifications
- does that mean if ssrFriendly
is not sent, then the children will render only after the sessionID is ready?
- if there is flag for ssrFriendly
, what is the behavior of ssrFriendly
set to false, and how does that affect how the hooks / rendering behave?
I personally think that there still room for the API to be further simplified tbh. I don't think it's a maintainable solution to choose ssrFriendly
as a term to build logic upon. If we explore the challenges in more detail, there are a few more fundamental aspects.
- Session ID state
a) loading - store not available yet [useSessionId => undefined
]
b) loading - store available, but async so not available yet [useSessionId => undefined
]
c) no previous session - so start new session [useSessionId => {newID}
]
d) previous session found - so resume [useSessionId => {idFetchedFromStorage}
]
- Rendering Children
a) option 1: render children when session ID is undefined
b) option 2: do not render children until session ID has value
- Hook behaviors
a) there is really only one option here - call APIs only after session ID is defined.
SUMMARY:
1. Session ID state should be as shared above
2. Children should always be rendered even if session ID is undefined
- hooks using session provided by convex-helpers
should always retuned undefined
(aka loading state) as long as the session ID is undefined
- if useSessionID
is called directly, it should return undefined and users will handle the loading state as with every other react hook that has some sort of async loading.
Thein the docs I see this ^ imo this is not possible if the intent of thesessionId
will only beundefined
when using SSR withssrFriendly
.
useStorage
parameter is intended to allow support for async storage. if we enforce that, it will very likely lead to an illogical behavior of the first render having a new session ID allocated, and then the second render having the correct session ID. by that time the other helpers may already have handled starting new sessions and handling API calls which would be incorrect I think..The statement is in regards to today without async storage. I think the version with async storage will have a little more nuance and different docs. The flag won't be
ssrFriendly
but rather the whole thing will assume an undeterminate amount of time until loading occurs.
Regarding the behavior / spec. Your (2) in your summary is the behavior. The current library will render all children. The useSessionId
hook will return undefined
.
- The useSessionQuery
will return undefined
(indistinguishable from loading) until the session is set and a query response
- The other hooks are async already, so they will just await the value and it will transparently work.
As for the state: this is only visible when you're using the lower-level useSessionId
hook. And yes, those state are all true.
If you don't specify ssrFriendly
(or you specify ssrFriendly={false}
as ssrFriendly
it is shorthand for ssrFriendly={true}
it will only be states (c) or (d):
- There will be no loading state or undefined
return, since there is no async store supported at this time.
- It will always return the value read from storage or a new ID in the first render.
If you do specify ssrFriendly
or ssrFriendly={true}
it'll be:
- undefined
on the first render.
- (c) or (d) on every subsequent render.
The goal is that the decision is "am I using sessions with SSR? If so, I should set ssrFriendly
on the provider and it will do the right thing with the normal hooks, though I'll have to handle undefined
if I use useSessionId
. If I'm not using SSR, it's all the same as before - I always get a value, and if I useSessionId
I don't have to worry about undefined
(I can assert it and expect the assertion to never fail)
Does that clarify things?
I think I led you astray saying it will work for async things in the future. While this model can also extend there eventually, it's not currently supported, for simplicity of mental model (and because there isn't an async store anyone's asked for).
So
does that mean if ssrFriendly is not sent, then the children will render only after the sessionID is ready?Children will always be rendered. If
ssrFriendly
is not set, then sessionId will be ready immediately.
In the future, with an async store, the main difference would be that you could provide an async store, and the docstring comment would change to
But I don't want more options for now.useSessionId
will returnundefined
on the first pass ifssrFriendly
is set, and until the value has been read, if using an async store.
Out of curiosity, what are you using sessions for? I just updated the post https://stack.convex.dev/anonymous-users-via-sessions
Anonymous Users via Sessions
Getting users to sign up for a new service before seeing any benefits is challenging. In this post, we looked at a couple of strategies for managing u...
If you don't specify ssrFriendly (or you specify ssrFriendly={false} as ssrFriendly it is shorthand for ssrFriendly={true} it will only be states (c) or (d)I assume that if this were true, the server side render for can only be a wrong value. if it is not undefined, it just means it will be a new session ID or some default value? I feel that there are 2 cases 1. if the user's code depends on session ID, then it will have to wait for session ID to be safe - which means it cannot be server rendered (otherwise the dependence on session ID will be logically incorrect - neither a random ID nor the default server session id value will be safe to use) 2. if the user's code doesn't depend on session ID, then regardless, it doesn't matter if the value is undefined. the better question is why the user would be getting the session ID in the first place. maybe there's something I'm still missing, but still not really seeing why there is a need to diverge the behaviors with the
ssrFriendly
flag.
regarding what I'm using sessions for - I'm actually doing super simple stuff. just creating a session at the start of the app. when the user logs in, associate the user ID with the session.
still not 100% sure given that session IDs are stored in local storage. but it's kinda good enough for now I think. approximately the same as storing a JWT in local storage.For (1) the difference is whether their code needs the session id on the client in order to do the first render. Server-rendering doesn’t fetch any convex data unless you do usePreloadedQuery so if the session id is only used by server queries & mutations & actions, then the client won’t be using the session id in any meaningful way on the client first render
Eg if you wanted to show the raw session id you’d need it to render. If you just make a query with the session id then it’s ok because the query will wait for the valid id anyways
do you have an example where the user would require the session ID to be populated with a non-
undefined
value, and with logic that still depends on it (even before local storage with the actual correct value is available)
if you rendered the raw session ID, then you would get a hydration error since the server's first render of the session ID would not be the same as the value from local storage I thinkThat is what ssrFriendly fixes
It renders undefined on the first client render
mm yeah. so I think my point is roughly that
ssrFriendly
is the only valid implementation.If you are using SSR, then, yes
If you are not using SSR it will be defined right away
haha okay I think I get it. mm
okay thanks. sorry that took a long while. so the main case that I wasn't considering, was for client only users who didn't want undefined. they would rather local storage be called immediately (not even within a
useEffect
) and then returned from the function.
is this correct? I believe this was quite close to the original implementation that was crashing previously when rendered on the server.Yes the one that crashed on the server tried to call local storage immediately. The reason it crashed was that the id generator couldn’t run server-side.
thanks @ian. btw, is there a way to use the
useSessionQuery
together with useStableQuery
? I'm wondering about composing the behaviors of the query hooks.
ideally I want something like react-query
, that allows me share the state across different places that are subscribed to the same data.
I was actually surprised that the useQuery
hook doesn't do this by default and still makes the round trip to the server. I assumed that one of the main benefits of queries being real time / subscribed would be that you could always have the latest state in one object, and thus as long as there is one subscriber, to hold on to the cached response in react.all instances of
useQuery
with the same arguments will return the same data, the queries are deduplicated and cached. both places will get the result in the same render
I'm not sure what logs you're seeing that show the opposite. If one component is unmounted before the other mounts, the subscription won't stick around, but you can use watchQuery
to continue watching the query as long as you want
As for how to compose another query wrapper with sessions, I added a useSessionIdArg
hook in v0.1.34
:
useStableQuery
essentially just says "hey if the parameters change, keep showing me the old result" which is sometimes incorrect. e.g. if the query is about returning data about a specific thing. However, if the query is search results, it's ok to show the results from the previous search query until the new data is therehey @ian sorry for the delay in my reply. to clarify, when we say that the queries are deduplicated and cached - is this a client side or server side cache? I can see that the request to the server is returning the response from the cache reliably, but I am also observing that the value from is being returned as
undefined
from a second subscription to the query.
and noted on the args, thanks a lot!If it’s the same query and same args then it is cached. However, when a component gets unmounted the cache is currently cleared. So if you mount A, unmount A, then mount B, it currently doesn’t maintain the subscription
You can use watchQuery on the lower level to hold a subscription open longer
mm okay makes sense. thanks!
Hey @ian - is the Stack post about this a bit outdated due to this thread? It doesn't seem like "SSR_DEFAULT" is still a thing, but the post states:
With this set, it will return a default SSR_DEFAULT(1) value on the first pass [...] (1) The value is "SSR default session ID" instead of an empty string or undefined in order to help you debug where an unexpected value is coming from, and avoid changing the return type to be string | undefined everywhere for non-SSR clienthttps://stack.convex.dev/track-sessions-without-cookies#using-ssr-with-sessions
Yes sorry the new behavior is in the docstring for the provider. I just updated the stack post. Thank you for the report!