#QRL serialized variable

11 messages · Page 1 of 1 (latest)

tawdry kestrel
#

Hiya...new qwik guy. I'm working on a library for qwik. The api is something like this:

const [state, send] = useMachine(
    {
      qrl: $(() =>
        noSerialize(
          machine({
            onCount(count) {
              context.count = count
            },
          }),
        ),
      ),
      initialState: noSerialize(machine({ count: 10 }).getState()),

      // Preferred way
      machine: noSerialize(machine({ count: 10 })),
    }
  )

the machine function returns a class, I can't serialize it. So I currently use qrl to send it, then internally I await it, but my component needs to have a default value, so I use initialState to provide the default value.

I'd prefer to just have only the third key though, where I pass machine()

Now Internally in useMachine function:


   const store = useStore({
    service: null,
  })

  useVisibleTask$(async ({ cleanup }) => {
    // Load the service
    const service = await qrl()
    service!.start()
    store.service = noSerialize(service)

    cleanup(() => {
      service!.stop()
    })
  })

  return useComputed$(() => store.service)

This is what works for me.
When I try just sending the machine() in the way I said I prefer. I want to do those tasks like start stop also, then I return it from here, I'm unable to use it outside.
E.g.

  const send = $((event: TEvent | string) => {
    service.value?.send(event)
  })

This service here is the returned useComputed$(() => store.service) in previous function, but within the send method it's seen as undefined.

Apparently:

Non-Serializable Variables Become undefined in QRLs

I'm just trying to find the best way to make this work with that desired API

Thanks

sage sequoia
#

ok, yes, noSerialize will not survive serializations, this is documented. So the system sends it as undefined. And you must init them on client.

#

To Init them, you must useVisibleTask$ to instantiate on browser and start using it

#

useComputed is like "instant" computation, will now wait for your await or resolutions from external resources.

#

useComputed will be excecuted again when there is a change on the signal so ... you can add a guard, a check to prevent the return if undefined

#

OR

#

you can use the useResource$

#

the docs says, Use useResource$() to create a computed value that is derived asynchronously.

foggy urchin
#

You can also do something like this:

type UrqlContext = Signal<NoSerialize<UrqlClient>>

const urqlCtx = createContextId<UrqlContext>('urql')
export const useUrqlProvider = () => {
    const rerunOnClient = useSignal(1)
    // eslint-disable-next-line qwik/no-use-visible-task
    useVisibleTask$(() => {
        rerunOnClient.value++
    })
    const urqlSig = useComputed$(
        () =>
            (rerunOnClient.value &&
                noSerialize(
                    createUrql({
                        url: apiUrl,
                        // we don't want caching, the data always needs to be fresh
                        exchanges: [fetchExchange],
                    })
                )) as NoSerialize<UrqlClient>
    )
    useContextProvider(urqlCtx, urqlSig)
}
export const useUrql = () => useContext(urqlCtx).value