# Custom Serializers ## Use cases Third party CustomClass implements something important and requires methods to work with the data. ## Simplest possible usage ### PROBLEM ```typescript export default component$(() => { const url = new URL(); // works! const value = new CustomClass(3434); // breaks serialization return ( <div onClick$={() => { console.log(url); console.log(value.method()); }} /> ) }); ``` ### CURRENT SOLUTION 0 (userspace) - :-1: state must be stored separate from CustomClass - :-1: user must know shape of state to call methods - :-1: state must be updated on every change - :-1: CustomClass must be checked + inited in every callback ```typescript import CustomClass from 'customClass' // USER PROVIDES THIS export const ensureClass = (theClass, obj) => { if (!obj.v) obj.v = noSerialize(new theClass( obj.state, // needs runtime serialization instead of once at pause time {updatePlainState: s=>obj.state=s} )) return obj } export default component$(() => { const url = new URL(); // works! // AWKWARD initialization const value = ensureClass(CustomClass, { state: 3434, }) return ( <div onClick$={ async () => { const [url, value] = await useLexicalScope(); // NEEDED IN ALL CALLBACKS ensureClass(CustomClass, value) console.log(url); console.log(value.v.method()); }} /> ) }); ``` ### SOLUTION 1 (changes useLexicalScope) - :+1: state is exactly what is expected - :+1: state is just the object - :+1: state gets serialized only when needed - :+1: CustomClass gets inited only on scope resume - customSerializer$ tags object with parse+stringify - when pausing, customSerializer-serializer stores QRL of both + stringified value - when resuming, useLexicalScope imports parser and parses value - useLexicalScope is async only on qwikloader calls - never from runtime calls - runtime call can preserialize everything - stringifier is needed on pause so either pause is async or needs pre-import - changes useLexicalScope - optimizer needs to write call differently - runtime needs to check scope for QRLs to load before calling handler ```typescript! import CustomClass from 'customClass' export default component$(() => { const url = new URL(); // works! // CustomClass defines .parse and .stringify const value = customSerializerQrl($(CustomClass), new CustomClass(3434)); return ( <div onClick$={ () => useLexicalScope((url, value) => { console.log(url); console.log(value.method()); })} /> ) }); ``` ### SOLUTION 2 (pure runtime) ```typescript import CustomClass from 'customClass' export default component$(() => { const url = new URL(); // works! const value: CustomSerialze<CustomClass> = customSerializerQrl($(CustomClass), new CustomClass(3434)); return ( <div onClick$={ async () => { const [url, value] = useLexicalScope(); console.log(url); const a = await value.resolve(); // added friction console.log(a.method()); }} /> ) }); ``` ### SOLUTION 3 (async) This breaks everything :) ```typescript import CustomClass from 'customClass' export default component$(() => { const url = new URL(); // works! const value: CustomSerialze<CustomClass> = customSerializerQrl($(CustomClass), new CustomClass(3434)); return ( <div onClick$={ async () => { const [url, value] = await useLexicalScope(); console.log(url); console.log(value.method()); }} /> ) }); ``` ## how qwikloader works when handler clicked ```json // could await import all parsers const mod = await import('path.js'); // could await import all parsers for this handler's scope const handler = mod['symbol']; globalContext = [element]; // could await parsers in handler // BUT must read context first handler(); globalContext = undefined; ```