Props must be serializable so that Qwik can resume and render each component independently from other components on the page. This possess a problem if we wish to pass a callback to a child component. Callbacks are functions and functions are not directly serializable, but they are serializable through the $() by converting them to QRLs first.
QRLs
Function passing across serializable boundaries must be done through QRLs. QRLs are serialized forms of a function. (See QRL in advanced section.)
Qwik has convenience APIs which end in $ that are equivalent to calling $() directly. These two lines are equivalent:
- inline:
<button onClick$={() => alert('click')}/> - explicit:
<button onClickQrl={$(() => alert('click'))}/>
Most of the time we use the first form as it allows us to inline our callbacks directly into the API. But at times it is necessary to use the second form so that we can separate where the function is declared and where it is used.
Declaring callback props
A component can declare a callback in its props by:
- Property that ends in
Qrl(as ingoodbyeQrl) - The type of the property is
QRL<T>whereTis the lazy reference type that the QRL points to (function signature).
The TypeScript automatically creates a <name>$:T property for every <name>Qrl:QRL<T> property. For example helloQrl: QRL<(name: string) => void> becomes hello$: (name: string) => void property.
This allows the user of <MyComponent> to use either the Qrl or $ form as shown here:
<MyComponent goodbyeQrl={goodbyeQrl} hello$={() => {...}} />
Using callback props
Notice that the <MyComponent> component can bind either directly to the prop QRL or wrap the QRL prop in another callback.
Passing the props.goodbyeQrl as a reference to the <button>
<button onClickQrl={props.goodbyeQrl}>hello</button>
Creating a new callback for <button> and internally invoking the callback QRL.
<button onClick$={async () => {
await props.helloQrl?.invoke('World');
}}>good bye</button>
This form allows the <button> to invoke the callback with custom parameters. Notice that the invocation requires async and await as the QRLs are lazy-loaded.
Understanding QRLs
Notice that it is only the creator of API that needs to understand QRLs. The user of the API can be oblivious to the fact that callbacks get converted into QRLs and that they need to be invoked by asynchronous means. As long as the user of API sticks to the properties ending in $ no special knowledge is required.