Synchronous Events with sync()

While not a common use case, you may occasionally need to process events synchronously.

The two most common use cases are to call event.preventDefault() or event.stopPropagation() on a DOM event. Those are handled by adding the preventdefault:eventname or stoppropagation:eventname in addition to your event handler.

  <a href="/" preventdefault:click onClick$={() => {
    console.log('clicked');
  }}>
    link
  </a>

When you have other use cases that require synchronous event processing, you can use either sync$() or useVisibleTask$().

  1. preferred way: use sync$() to embed the code in the HTML. Fast, resumable, but with some restrictions (see below).
  2. eager registration: use useVisibleTask$() to register a handler. No restrictions, but it will not handle events until the visible task has executed, and it will cause of course run extra javaScript while the page is loading.
  <a href="/" 
    onClick$={sync$((event, target) => {
      // Only prevent default if the ctrl key is pressed.
      if (event.ctrlKey) {
        event.preventDefault();
      }
    })}>
    link
  </a>

sync$() Restrictions

The sync$() function is a resumable way to process events synchronously. However, it has some significant restrictions. The sync$() can't close over anything. The implications are that you can't:

  • access any state: The recommended way to get the state into function is to place on element attributes.
  • access any non-browser functions: No imports or closing over any variables or functions are allowed.
  • the function should be small as it will get inlined into the resulting HTML.

For this reason, we recommended breaking up the event handlers into two parts:

  1. sync$(): The part which must be synchronous. This part should be small and not close over anything.
  2. $(): The part which can be asynchronous. This part can be large and can close over anything including the state.
  <a href="/" 
    onClick$={[
      sync$((event, target) => {
        // This part is synchronous and can't close over anything.
        if (event.ctrlKey) {
          event.preventDefault();
        }
      }),
      $(() => {
        // This part can be asynchronous and can close over anything.
        console.log('clicked');
      })
    ]}>
    link
  </a>

You can also dispatch a custom event from the sync$() handler and handle it asynchronously:

  <a href="/" 
    onClick$={sync$((event, target) => {
      if (event.ctrlKey) {
        event.preventDefault();
      }
      // Dispatch a custom event to be handled asynchronously.
      target.dispatchEvent(new CustomEvent('myclick', { bubbles: true }));
    })}
    onMyclick$={() => {
      // This part can be asynchronous and can close over anything.
      console.log('clicked');
    }}>
    link
  </a>

Your task: Convert the onClick$ from asynchronous event to synchronous event by using sync$.

Edit Tutorial