sync$() Synchronous Events (BETA)

Qwik processes events asynchronously. This means that some APIs such as event.preventDefault() and event.stopPropagation() do not work as expected. To work around this limitation, Qwik provides a sync$() API which allows you to process events synchronously. But sync$() comes with a few caveats:

  1. sync$() can't close over any state.
  2. sync$() can't call other functions which are declared in scope or imported.
  3. sync$() is serialized into HTML and therefore we should be conscious of the size of the function.

A typical way to deal with these limitations is to split the event handling into two parts:

  1. sync$() which is called synchronously and can call methods such as event.preventDefault() and event.stopPropagation().
  2. $() which is called asynchronously and can close over the state and call other functions, and has no restriction on the size.

Because sync$() can't access the state what is the best strategy to deal with it? The answer is to use element attributes to pass state into the sync$() function.

NOTE: If you only need to prevent the default behavior, you can simply use the standard preventDefault:{eventName} syntax. This is strictly for when you need to chain events together synchronously

Example: sync$() with state

In this example, we have a behavior where we want to prevent the default behavior of the link based on some state. We do this by breaking the code into three parts:

  1. sync$(): a synchronous portion that is kept to the minimum,
  2. $(): an asynchronous portion that can be arbitrarily large, and can close over state,
  3. data-should-prevent-default: an attribute on the element that is used to pass state into the sync$() function.
import { component$, useSignal, sync$, $ } from '@builder.io/qwik';
 
export default component$(() => {
  const shouldPreventDefault = useSignal(true);
  return (
    <div>
      <div>Sync Event:</div>
      <input
        type="checkbox"
        checked={shouldPreventDefault.value}
        onChange$={(e, target) =>
          (shouldPreventDefault.value = target.checked)
        }
      />{' '}
      Should Prevent Default
      <hr />
      <a
        href="https://google.com"
        target="_blank"
        data-should-prevent-default={shouldPreventDefault.value}
        onClick$={[
          sync$((e: MouseEvent, target: HTMLAnchorElement) => {
            if (target.hasAttribute('data-should-prevent-default')) {
              e.preventDefault();
            }
          }),
          $(() => {
            console.log(
              shouldPreventDefault.value ? 'Prevented' : 'Not Prevented'
            );
          }),
        ]}
      >
        open Google
      </a>
    </div>
  );
});

Contributors

Thanks to all the contributors who have helped make this documentation better!

  • mhevery
  • RumNCodeDev