S
SolidJS•2mo ago
Tom

Context lost on HMR

I'm having an issue with the state of a Solid context getting lost on HMR. This makes sense because the module that calls createContext is being reloaded, and the call is a module level statement, which is pretty normal I think. But from this https://github.com/solidjs/solid-refresh/issues/15 it looks like solid-refresh should know how to preserve the state. Is there some good practive that I'm missing to make this work?
GitHub
Contexts become undefined during HMR · Issue #15 · solidjs/solid-re...
It seems during HMR any useContext() becomes undefined, potentially breaking components that trust on a context being always unavailable or set up without defaults, which in turn breaks the hot rel...
13 Replies
deluksic
deluksic•2mo ago
Reloading the module causes the context object to change identity. Try splitting out the createContext into a separate definition file, this should help.
Tom
TomOP•2mo ago
It's already in a different file but that file is getting reloaded when I make a change to a component file that (indirectly) imports it. I guess I need to understand more about how vite HMR decides which modules to reload. I solved this (well, me and Claude 3.5 solved it). In case it's helpful to others, here's the solution (I which discord would publish these threads on the web so this stuff was more findable)
export function createHotStableContext<T>(name: string, defaultValue?: T): Context<T> {
const contextKey = `hot-context-${name}`

const {hot} = import.meta
if (hot) {
return hot.data[contextKey] ??= createContext<T>(defaultValue as T)
} else {
return createContext<T>(defaultValue as T)
}
}

export const MyContext = createHotStableContext<MyType>('MyContext')
export function createHotStableContext<T>(name: string, defaultValue?: T): Context<T> {
const contextKey = `hot-context-${name}`

const {hot} = import.meta
if (hot) {
return hot.data[contextKey] ??= createContext<T>(defaultValue as T)
} else {
return createContext<T>(defaultValue as T)
}
}

export const MyContext = createHotStableContext<MyType>('MyContext')
lxsmnsyc
lxsmnsyc•2mo ago
if you have the context creation in the same file as the useContext call, the bug can occur which is why I always recommend isolating createContext into a separate file this is too unsafe for a fix
Tom
TomOP•2mo ago
I'm not understanding how this helps, because vite follows the import chains and reloads those modules too. I already have it in a separate file. Ohh sorry I scanned what you said too quickly I do have the create and use in the same file - I'll try separating
lxsmnsyc
lxsmnsyc•2mo ago
so that createContext doesn't have to re-run on a TSX file, which causes different calls. TBF HMR for createContext is supported (useContext actually looks up by context ID so the HMR just carries over the already created one) but I guess the file extension is limited. I might have to re-imagine things here, probably needs discussion with Ryan
Tom
TomOP•2mo ago
I tried moving the createContext into a different file (.ts) and I'm still losing state on hot reload A console.log in the new file with createContext shows that the file is getting reloaded - not sure if that's expected or not
deluksic
deluksic•2mo ago
Also make sure there's no import cycles They can destroy hmr completely
Tom
TomOP•2mo ago
Yes - I got rid of all those recently for exactly that reason @lxsmnsyc 🤖 anything else I can try, given moving createContext to a .ts did not help?
lxsmnsyc
lxsmnsyc•2mo ago
what does your file with createContext look like honestly it always worked for me
Tom
TomOP•2mo ago
I've reverted back to my createHotStableContext now, but what I tried was a one-line module, just export const MyContext = createContext() I think I might still have issues with import cycles. I thought I could use dynamic imports to avoid them, but it looks like any kind of import cycle is a problem, not just static ones. It's tricky because my UI is fundamentally recursive (tree editor). I will look into dependency injection to resolve
lxsmnsyc
lxsmnsyc•2mo ago
I don't see how this would not work except if you have cyclic imports, as you mentioned once a module is unchanged it will always remain unchanged
Tom
TomOP•2mo ago
Doesn't HMR re-execute all recursively included modules, even if they haven't changed?
lxsmnsyc
lxsmnsyc•2mo ago
no, it only reloads the changed module (or those modules that actually wanted to be reloaded) by default, it doesn't reload unchanged modules
Want results from more Discord servers?
Add your server