Custom Serializers

Use cases

Third party CustomClass implements something important and requires methods to work with the data.

Simplest possible usage

PROBLEM

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)

  • Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More โ†’
    state must be stored separate from CustomClass
  • Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More โ†’
    user must know shape of state to call methods
  • Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More โ†’
    state must be updated on every change
  • Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More โ†’
    CustomClass must be checked + inited in every callback
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)

  • Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More โ†’
    state is exactly what is expected
  • Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More โ†’
    state is just the object
  • Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More โ†’
    state gets serialized only when needed
  • Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More โ†’
    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
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)

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 :)

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

// 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;