basic progressively enhanced counter not working

In my project I was running into an issue where I was getting the initial state of a variable so I made a minimal reproduction
// src/routes/index.tsx
import { useRouteData } from "solid-start"
import { createServerAction$, createServerData$, redirect } from "solid-start/server"

let count = 0

export const routeData = () => createServerData$(() => count)

export default () => {
const getRouteData = useRouteData<typeof routeData>()
const [ _, increment ] = createServerAction$(async (form: FormData) => {
count++
return redirect("/")
})

return <increment.Form><input type="submit" value={getRouteData()}/></increment.Form>
}
// src/routes/index.tsx
import { useRouteData } from "solid-start"
import { createServerAction$, createServerData$, redirect } from "solid-start/server"

let count = 0

export const routeData = () => createServerData$(() => count)

export default () => {
const getRouteData = useRouteData<typeof routeData>()
const [ _, increment ] = createServerAction$(async (form: FormData) => {
count++
return redirect("/")
})

return <increment.Form><input type="submit" value={getRouteData()}/></increment.Form>
}
when javascript is disabled it always displays "0" and when javascript is enabled, it initially shows "0" and then becomes the number it's supposed to be after being clicked I looked into the produced code and for some reason the count variable is duplicated and in some cases the first one is used and in other cases the duplicate is being used
4 Replies
Samual 🦢
Samual 🦢•14mo ago
// dist/server.js
// ...
let count$1 = 0;
const $$server_module0$1 = server$.createHandler(async function $$serverHandler0() {
return count$1;
}, "/_m/0dbe216f23/routeData", true);
server$.registerHandler("/_m/0dbe216f23/routeData", $$server_module0$1);
const routeData = () => createRouteData($$server_module0$1);
const $$server_module1$1 = server$.createHandler(async function $$serverHandler1(form) {
count$1++;
return redirect("/");
}, "/_m/90d4313cf1/_", true);
server$.registerHandler("/_m/90d4313cf1/_", $$server_module1$1);
// ...
const _tmpl$ = ["<input", " type=\"submit\"", ">"];
let count = 0;
const $$server_module0 = server$.createHandler(async function $$serverHandler0() {
return count;
}, "/_m/0dbe216f23/routeData", true);
server$.registerHandler("/_m/0dbe216f23/routeData", $$server_module0);
const $$server_module1 = server$.createHandler(async function $$serverHandler1(form) {
count++;
return redirect("/");
}, "/_m/90d4313cf1/_", true);
server$.registerHandler("/_m/90d4313cf1/_", $$server_module1);
const routeData1 = (() => {
const getRouteData = useRouteData();
const [_, increment] = createRouteAction($$server_module1);
return createComponent(increment.Form, {
get children() {
return ssr(_tmpl$, ssrHydrationKey(), ssrAttribute("value", escape(getRouteData(), true), false));
}
});
});
// ...
// dist/server.js
// ...
let count$1 = 0;
const $$server_module0$1 = server$.createHandler(async function $$serverHandler0() {
return count$1;
}, "/_m/0dbe216f23/routeData", true);
server$.registerHandler("/_m/0dbe216f23/routeData", $$server_module0$1);
const routeData = () => createRouteData($$server_module0$1);
const $$server_module1$1 = server$.createHandler(async function $$serverHandler1(form) {
count$1++;
return redirect("/");
}, "/_m/90d4313cf1/_", true);
server$.registerHandler("/_m/90d4313cf1/_", $$server_module1$1);
// ...
const _tmpl$ = ["<input", " type=\"submit\"", ">"];
let count = 0;
const $$server_module0 = server$.createHandler(async function $$serverHandler0() {
return count;
}, "/_m/0dbe216f23/routeData", true);
server$.registerHandler("/_m/0dbe216f23/routeData", $$server_module0);
const $$server_module1 = server$.createHandler(async function $$serverHandler1(form) {
count++;
return redirect("/");
}, "/_m/90d4313cf1/_", true);
server$.registerHandler("/_m/90d4313cf1/_", $$server_module1);
const routeData1 = (() => {
const getRouteData = useRouteData();
const [_, increment] = createRouteAction($$server_module1);
return createComponent(increment.Form, {
get children() {
return ssr(_tmpl$, ssrHydrationKey(), ssrAttribute("value", escape(getRouteData(), true), false));
}
});
});
// ...
I assume this is a bug, though I might also be using the api wrong here's a repro repo https://github.com/samualtnorman/solid-start-bug.git
mdynnl
mdynnl•13mo ago
global state in SSR is not recommended because then every request regardless of which devices uses the same state, aka global state pollution, to make thr matter worse, now it spans across every request if you want to persist data, any kind of storage, kv, relational exists and that state can be shared via a context if you really want it that way, extract it into module, so the server compilation uses the same reference even after bundling e.g
export default { foo: 0 }
export const bar = { value: 0 }
export default { foo: 0 }
export const bar = { value: 0 }
congrats, you have an server app that works across requests (i dont have a clue how this'd work in a cloud function platform though)
Samual 🦢
Samual 🦢•13mo ago
this is running on a good old fashioned self hosted server I ran into this during development before I set up a real db I thought for development it'd be good enough to temporarily use global state to simulate a db how come putting the global state into a module forces the same variable to be used but not causes the variable to be duplicated?
mdynnl
mdynnl•13mo ago
routeData is extracted into its own module (hoisted for eager data loading which file system router uses it for setting up routes) and every identifier it uses comes along with it when bundled causes duplication maybe routeData could also live inside the original route module as an export but i'm not sure about the interop if the module has side effects