[🐞] Strange behavior and errors using closures and props
Which component is affected?
Qwik Runtime
Describe the bug
I'm creating a custom hook that returns a closure:
export const useFn = () => {
const state = useContext(myContext);
return (val: number) => {
return (state.value + val).toString();
};
};
The function is pure: it depends only on the parameter and always returns the same result, because myContext is static and immutable:
useContextProvider(myContext, {
value: 0,
});
I use the function like this::
export const MyComponent = component$((props: IMyComponent) => {
const count = useSignal(0);
const c = useFn();
return (
<>
<p>{props.val}</p>
<p>{c(count.value)}</p>
</>
);
});
Then I call MyComponent with another closure in the props:
export default component$(() => {
const c = useFn();
return (
<div>
<MyComponent val={c(1)} />
</div>
);
});
I get the following error:
[vite] Internal server error: Code(3): Only primitive and object literals can be serialized
After numerous attempts, I discovered these situations:
- if i remove the function from the props, everything works
- if I leave the function in the props, and I don't pass the signal value to the function in the child component, everything works
- if I rename the function to
makeFn(without initial use*) everything works (except the linter)
It is incomprehensible to me why the props of the parent component and the signal as a parameter in the child component are mutually exclusive.
Thanks
Reproduction
https://stackblitz.com/edit/qwik-starter-vdcrdc?file=src%2Froutes%2Findex.tsx
Steps to reproduce
No response
System Info
Not relevant
Additional Information
If I inspect the production files, I can see that in the case of use*, the function is considered immutable yes, but included in a derived signal _fnSignal:
const s_xYL1qOwPyDI = () => {
const c = useFn();
return /* @__PURE__ */ _jsxQ("div", null, null, /* @__PURE__ */ _jsxC(MyComponent, {
get val() {
return c(1);
},
[_IMMUTABLE]: {
val: _fnSignal((p0) => p0(1), [
c
], "p0(1)")
}
}, 3, "H1_1"), 1, "H1_2");
};
In the case of the function called with make*, instead it remains as is and everything works:
const s_xYL1qOwPyDI = () => {
const c = makeFn();
return /* @__PURE__ */ _jsxQ("div", null, null, /* @__PURE__ */ _jsxC(MyComponent, {
val: c(1)
}, 3, "H1_1"), 1, "H1_2");
};
Qwik requires that all of the state is serializable. A function closure is not serializable. To make it serializable, you need to wrap it with $()
export const useFn = () => {
const state = useContext(myContext);
return $((val: number) => {
return (state.value + val).toString();
});
};
this should do the trick.
I am going to close this issue because it is working as intended.