You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
//+layout.tsimporttype{ContextLoad,LayoutLoad}from'./$types.js';exportconstcontext=({parent_context, url, ...rest})=>{constauth=newAuthService(/*parameters*/);constanalytics=newAnalyticsService(/*parameters*/);return{services: {auth: instantiate_auth_service(/*Some parameters*/);async[Symbol.asyncDispose](){awaitanalytics.flush();analytics.close_ws();}}})satisfiesContextLoad;exportconstload=(({context})=>{//context includes the value returned from the context function defined above/*Code here*/})satisfiesLayoutLoad;
Why it is necessary
As a mechanism for dependency injection and dependency management.
Better modularization primitives.
As an adapter-static enjoyer I abuse load functions to do this but it is not semantically coherent.
I am accessing services through a prop called data that the docs say should hold data, not services.
I also don't like abusing APIs like this because I know another devs will copy it without fully understanding its implications, things will break because they use adapter-node and they will proceed as explained in the following point.
Frameworks not giving you good abstractions result in either the wrong abstraction being used, a "clever" workaround being used (see point 3) or a new abstraction being created by the user. Depending on the user experience and skills the new abstraction can be good, mid, hacky, buggy, ai-generated or will make no sense at all (like using global state).
Standardization and normalization. "How do I X with Y framework? => read the docs" instead of "How do I X with Y framework? => we did it by creating an instance of RubeGoldberg2 then calling the static apply method on it but the team lead says we should use the RubeGoldberg4 class as the RubeGoldberg class was deprecated many years ago due to the many bugs it contains. Unfortunately the RubeGoldberg4 class does not support your use-case yet so you may want to use RubeGoldberg3V2 and change it when we update RubeGoldberg4, just make sure you delete the "DO_NOT_USE_RG3V2_OR_YOU_WILL_BE_FIRED" global variable immediately after calling the apply method because there is a test in CI that will break" .
Can be used in both, universal and server files, no need to worry about return types being serializable as you won't be sending contexts over the net.
How it works
Similarly to load functions, values returned by stacked contexts are merged, and load functions have access to the value resulting from their sibling context function (merged with parent contexts).
+*.server.ts files do not share context nor context type with non +*.server.ts files and vice-versa.
+page.svelte and +layout.svelte get a context prop along with data, the context prop always refers to "universal" context (as in universal load functions) in .svelte files as it makes no sense to refer to the server context there.
Symbol.dispose and Symbol.asyncDispose are both called (if defined) when we navigate away from the corresponding page/layout so we can clean the mess (e.g: gracefully closing ws, clearing some localStorage keys used only to persist some form state, removing global event listeners, etc.).
Docs
First of all note that I only use adapter-static so I might be missing some edge cases here.
Docs should show a very big and red warning stating that server and client context are separate so doing context.clicked+=1 inside an onclick handler will not magically update the value of context.clicked in the server and might result in hydration mismatches.
Docs should recommend using pure context functions unless you are 100% sure your context won't be ever ran in both, client and server.
Docs should discourage returning context data from load functions.
//+layout.tsimporttype{ContextLoad,LayoutLoad}from'./$types.js';exportconstcontext=({parent_context, url, ...rest})=>{constauth=newAuthService(/*parameters*/);constanalytics=newAnalyticsService(/*parameters*/);return{services: {auth: instantiate_auth_service(/*Some parameters*/);async[Symbol.asyncDispose](){awaitanalytics.flush();analytics.close_ws();}}})satisfiesContextLoad;exportconstload=(async({context})=>{//context includes the value returned from the context function defined aboveconstauth_result=awaitcontext.services.auth.login(/*Some params*/);//GOODreturn{auth: context.services.auth,//BAD, service is already available under the context prop and might not be serializable.account: auth_result//GOOD};})satisfiesLayoutLoad;
I only have negative opinions in regard to that context.
I have never used it and I don't think I ever will. I even prefer using load functions for context management to using the existing context API.
Some of those my opinions against the current context API are:
I don't want to initialize services that start WS connections just because there are 2-3 pages that are rarely visited that really need it. I don't want to code a workaround to initialize them lazily and then manually de-initialize them when the user navigates elsewhere.
I don't want to get the context via getContext('key'). I am too dumb to remember what I named my context and too lazy to type its full name as a string. I want to write a letter and hit autocomplete to access my context. I want my context to be strongly typed out of the box so I can keep hitting autocomplete without ending up calling some service that is not really there or has not been initialized or even getting no context at all because I mispelled the context name.
I don't want to initialize things like an error reporting service globally while it is only used in a few pages. I don't want to remember how the context having the error reporting service is named. (see point 4 in "Why it is important")
I don't want to write code to make this context fit my needs. (see point 4 in "Why it is important")
I don't want to define a context per every combination of services I may use on any given page. That is not maintainable. I just want to specify which services are needed by layout and by page and get the context automagically merged and built, overriding parent services if necessary (for example overriding the account service on a layout meant for authenticated users so it includes methods only available to those users).
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
TL;DR: What it is
Why it is necessary
adapter-static
enjoyer I abuseload
functions to do this but it is not semantically coherent.I am accessing services through a prop called
data
that the docs say should hold data, not services.I also don't like abusing APIs like this because I know another devs will copy it without fully understanding its implications, things will break because they use
adapter-node
and they will proceed as explained in the following point.RubeGoldberg2
then calling the staticapply
method on it but the team lead says we should use theRubeGoldberg4
class as theRubeGoldberg
class was deprecated many years ago due to the many bugs it contains. Unfortunately theRubeGoldberg4
class does not support your use-case yet so you may want to useRubeGoldberg3V2
and change it when we updateRubeGoldberg4
, just make sure you delete the "DO_NOT_USE_RG3V2_OR_YOU_WILL_BE_FIRED" global variable immediately after calling theapply
method because there is a test in CI that will break" .How it works
Similarly to
load
functions, values returned by stacked contexts are merged, andload
functions have access to the value resulting from their siblingcontext
function (merged with parent contexts).+*.server.ts
files do not share context nor context type with non+*.server.ts
files and vice-versa.+page.svelte
and+layout.svelte
get acontext
prop along withdata
, thecontext
prop always refers to "universal" context (as in universal load functions) in.svelte
files as it makes no sense to refer to the server context there.Symbol.dispose
andSymbol.asyncDispose
are both called (if defined) when we navigate away from the corresponding page/layout so we can clean the mess (e.g: gracefully closing ws, clearing somelocalStorage
keys used only to persist some form state, removing global event listeners, etc.).Docs
First of all note that I only use
adapter-static
so I might be missing some edge cases here.Docs should show a very big and red warning stating that server and client context are separate so doing
context.clicked+=1
inside an onclick handler will not magically update the value ofcontext.clicked
in the server and might result in hydration mismatches.Docs should recommend using pure context functions unless you are 100% sure your context won't be ever ran in both, client and server.
Docs should discourage returning context data from load functions.
What about the current thing called context in svelte
I only have negative opinions in regard to that context.
I have never used it and I don't think I ever will. I even prefer using load functions for context management to using the existing context API.
Some of those my opinions against the current context API are:
getContext('key')
. I am too dumb to remember what I named my context and too lazy to type its full name as a string. I want to write a letter and hit autocomplete to access my context. I want my context to be strongly typed out of the box so I can keep hitting autocomplete without ending up calling some service that is not really there or has not been initialized or even getting no context at all because I mispelled the context name.account
service on a layout meant for authenticated users so it includes methods only available to those users).Beta Was this translation helpful? Give feedback.
All reactions