“use server”; Is Breaking Everything In My App

I’ve got one post going that touches on this subject of “use server”; breaking my app: A New Way to Chat with Friends & Communities I’ve been working on this for days at this point. If I use “use server”; on its own, it works. If I use client side code on its own, it works. But any time I try to pass data between the client and the server everything breaks. My last comment in the post above uses the example right out of the docs to show the errors. I get a long list of errors. $R reference errors, internal server errors, Uncaught type errors, etc. Basically, I can only work with the server OR the client, but not both at the same time. Is this expected behavior? Or are we supposed to be able to work with server-side and client-side code at the same time? For clarity sake, here’s a dead simple example. Here, clicking the “Run Function” button causes “Error from functionTwo” to be printed to the console. This is expected behavior.
export default function MyComponent() {

async function functionOne() {
try {
await functionTwo();
} catch (error: any) {
console.log(error.message);
}
}

async function functionTwo() {
throw new Error("Error from functionTwo");
}

return (
<button onClick={functionOne}>
Run Function
</button>
);
}
export default function MyComponent() {

async function functionOne() {
try {
await functionTwo();
} catch (error: any) {
console.log(error.message);
}
}

async function functionTwo() {
throw new Error("Error from functionTwo");
}

return (
<button onClick={functionOne}>
Run Function
</button>
);
}
But, when I add “use server”; to functionOne, it breaks.
export default function MyComponent() {

async function functionOne() {
"use server";
try {
await functionTwo();
} catch (error: any) {
console.log(error.message);
}
}

async function functionTwo() {
throw new Error("Error from functionTwo");
}

return (
<button onClick={functionOne}>
Run Function
</button>
);
}
export default function MyComponent() {

async function functionOne() {
"use server";
try {
await functionTwo();
} catch (error: any) {
console.log(error.message);
}
}

async function functionTwo() {
throw new Error("Error from functionTwo");
}

return (
<button onClick={functionOne}>
Run Function
</button>
);
}
Here’s the error:
chunk-6APUFJ5X.js:864 Uncaught TypeError: handler.call is not a function
at HTMLDocument.eventHandler
chunk-6APUFJ5X.js:864 Uncaught TypeError: handler.call is not a function
at HTMLDocument.eventHandler
24 Replies
brenelz
brenelz12mo ago
I think you want to have your async functions top level
ChrisThornham
ChrisThornhamOP12mo ago
@brenelz By "top level" I think you mean moving my async functions outside of the component... Assuming that is correct, no change. I still get the same error. Here's the code:
async function functionOne() {
"use server";
try {
await functionTwo();
} catch (error: any) {
console.log(error.message);
}
}

async function functionTwo() {
throw new Error("Error from functionTwo");
}

export default function MyComponent() {
return (
<button onClick={functionOne}>
Run Function
</button>
);
}
async function functionOne() {
"use server";
try {
await functionTwo();
} catch (error: any) {
console.log(error.message);
}
}

async function functionTwo() {
throw new Error("Error from functionTwo");
}

export default function MyComponent() {
return (
<button onClick={functionOne}>
Run Function
</button>
);
}
@brenelz Even when I followed your "useSubmission" advice, things are still not working: See this example: https://discord.com/channels/722131463138705510/1191488356245307402/1191810506500100156 Does the code in this example work for you?
brenelz
brenelz12mo ago
So you need to make one tweak to your onclick. onClick={() => functionOne()} Then I get $R not defined which is definitely a bug If you turn ssr: false in vite.config.ts it doesn't error which is a temp workaround
lxsmnsyc
lxsmnsyc12mo ago
I may be repeating it too much but $R is already resolved in the next patch release
brenelz
brenelz12mo ago
Yeah I know it keeps coming up as people hit it
Brendonovich
Brendonovich12mo ago
Would this not be because functionTwo doesn’t have use server, and instead is being treated as a client function?
lxsmnsyc
lxsmnsyc12mo ago
no it's an isomorphic function it exists on both client and server
ChrisThornham
ChrisThornhamOP12mo ago
Sorry for my ignorance here. You’ve mentioned this “patch” a few times, but I don't know what that means. Can I assume all $R errors are a bug that will be fixed by this patch? If so, that makes life a lot easier.
lxsmnsyc
lxsmnsyc12mo ago
GitHub
Fix global $R by lxsmnsyc · Pull Request #1188 · solidjs/solid-st...
A mistake we made is that $R only generates if we are actually serializing any data on the server. The mistake here is that we won't know if a server function, which is dependent on the same va...
ChrisThornham
ChrisThornhamOP12mo ago
@brenelz That worked! Thank you. One question... In Solid, I've always called functions in onClick without the arrow function (unless I need to pass a variable). Is that different in SolidStart? How did you know to use the arrow function here? For reference, here's what the Solidjs docs say about using onClick. As you can see, there's no arrow function.
function Counter() {
const [count, setCount] = createSignal(0);
const increment = () => {
setCount(count() + 1);
};
return (
<div>
Current count: {count()}
<button onClick={increment}>Increment</button>
</div>
);
}
function Counter() {
const [count, setCount] = createSignal(0);
const increment = () => {
setCount(count() + 1);
};
return (
<div>
Current count: {count()}
<button onClick={increment}>Increment</button>
</div>
);
}
Thank you!
lxsmnsyc
lxsmnsyc12mo ago
the issue is that when you use the server function directly, the event object (which is passed to the listener) is also getting attempted to be sent to the server function. In some cases that may work, however if the event is proved to be "unserializable", it will cause errors
ChrisThornham
ChrisThornhamOP12mo ago
So is it safe to say that I should use arrow function syntax when calling server functions from event handlers?
lxsmnsyc
lxsmnsyc12mo ago
the syntax don't matter. What matters is you don't pass the event object to the server function
ChrisThornham
ChrisThornhamOP12mo ago
Oh! So it would be better to call a client-side function from the onClick handler... and then call the server function from that client-side function? For anyone following this or reading it in the future, here's the corrected code. By adding the handleClick() function, you avoid passing the event object to the server function.
async function functionOne() {
"use server";
try {
await functionTwo();
} catch (error: any) {
console.log(error.message);
}
}

async function functionTwo() {
throw new Error("Error from functionTwo");
}

export default function MyComponent() {

async function handleClick() {
await functionOne();
}

return (
<button onClick={handleClick}>
Run Function
</button>
);
}
async function functionOne() {
"use server";
try {
await functionTwo();
} catch (error: any) {
console.log(error.message);
}
}

async function functionTwo() {
throw new Error("Error from functionTwo");
}

export default function MyComponent() {

async function handleClick() {
await functionOne();
}

return (
<button onClick={handleClick}>
Run Function
</button>
);
}
Metru
Metru12mo ago
I've run into this error quite a few times 😩 Along the lines of "Seroval can't serialize function" Hopefully we'll get a better error message in the future.
lxsmnsyc
lxsmnsyc12mo ago
like in what way?
Metru
Metru12mo ago
First time I saw it, it was kind of cryptic and I didn't know what needed to be changed. Eventually enough messing around got it working. But just now that you mentioned it, it makes sense that the event object would be passed along, even if its not being used (e.g. onclick to trigger a function with no args, even though onclick has an Event object.) Maybe at the very least, some docs explaining that onClick={() => myServerFunction()} is preferred over onClick={myServerFunction} for the above reasons
ChrisThornham
ChrisThornhamOP12mo ago
Or if I'm reading what @lxsmnsyc 🤖 is saying correctly, it would be helpful to say “don’t call server functions directly from event handlers. Instead call client-side functions that call server functions.” I think this would add a lot of clarity to the docs.
Metru
Metru12mo ago
Does useAction make a difference here? my guess is no, but I'm curious if it should e.g.
const useFunctionOne = useAction(functionOne)
//...
<button onClick={useFunctionOne}>...
const useFunctionOne = useAction(functionOne)
//...
<button onClick={useFunctionOne}>...
ChrisThornham
ChrisThornhamOP12mo ago
I got rid of useAction altogether in my app. I'm just exporting server functions from a server file and calling them in my components when needed.
Metru
Metru12mo ago
I'm pretty much doing the same thing To your question earlier, in my app, I'm doing SSR but once the page is loaded, its all CSR, minus the occasional form action. Funny enough I'm migrating it to supabase, so we'll probably have a very similar tech stack by tomorrow.
lxsmnsyc
lxsmnsyc12mo ago
that's one thing. There's a plethora of data types that you cannot pass to server functions, as mentioned in the docs. that's a good tip
Metru
Metru12mo ago
I'll have to check again, seems I missed that part. BTW, didn't see any issues on gh about docs but still wondering if help is needed in that department.
ChrisThornham
ChrisThornhamOP12mo ago
Apparently, I missed that part too. It's all pretty new to me, but if you have any Supabase questions, let me know. I've got a pretty good basic Auth system set up using email & password and Google sign in.
Want results from more Discord servers?
Add your server