forked from QwikDev/qwik
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(qwik): Experimental support for synchronous QRL
sync$()
. (Qwik…
…Dev#5545) * feat(qwik): Experimental support for synchronous QRL `sync$()`. It is often desirable to have call API on events synchronously. For example, `event.preventDefault()` and `event.stopPropagation()` must be called synchronously in order to have a helpful effect. ```TypeScript <button onClick$={[ $sync(event => event.preventDefault()), $(() => { // normal behavior. }) ]}>Click Me</button> ``` The idea is that `$sync()` will be inlined into HTML. For this reason, the function, `$sync()`, must not only be a pure function but also can not depend on any external code, such as imports. The best way to think about it is that the function inside `$sync()` must be able to survive `fn.toString()` and then be reconstructed from the string into a proper function. In practice, this means that the function must be a simple function which can't: - Close over any state. - Close over any imports. Fix QwikDev#5322 Fix QwikDev#4496 * fix(qrl.ts): dedupe sync$ (QwikDev#5566) * fix(qrl.ts): dedupe sync$ * refactor(core): sync$ inlineFns key * feat(qwik): Experimental support for synchronous QRL `sync$()`. It is often desirable to have call API on events synchronously. For example, `event.preventDefault()` and `event.stopPropagation()` must be called synchronously in order to have a helpful effect. ```TypeScript <button onClick$={[ $sync(event => event.preventDefault()), $(() => { // normal behavior. }) ]}>Click Me</button> ``` The idea is that `$sync()` will be inlined into HTML. For this reason, the function, `$sync()`, must not only be a pure function but also can not depend on any external code, such as imports. The best way to think about it is that the function inside `$sync()` must be able to survive `fn.toString()` and then be reconstructed from the string into a proper function. In practice, this means that the function must be a simple function which can't: - Close over any state. - Close over any imports. Fix QwikDev#5322 Fix QwikDev#4496 --------- Co-authored-by: PatrickJS <github@patrickjs.com>
- Loading branch information
Showing
59 changed files
with
1,036 additions
and
220 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
etc | ||
temp | ||
.history | ||
.lh | ||
*. | ||
**/*.log | ||
*.node | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
packages/docs/src/routes/demo/cookbook/sync-event/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
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> | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
packages/docs/src/routes/docs/cookbook/sync-events/index.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
--- | ||
title: Cookbook | Synchronous Events with State | ||
contributors: | ||
- mhevery | ||
--- | ||
|
||
import CodeSandbox, {CodeFile} from '../../../../components/code-sandbox/index.tsx'; | ||
|
||
|
||
# `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. | ||
|
||
## 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. | ||
|
||
|
||
<CodeSandbox url="/demo/cookbook/sync-event/" style={{ height: '15em' }}> | ||
</CodeSandbox> | ||
|
||
|
||
<CodeFile src="/src/routes/demo/cookbook/sync-event/index.tsx"> | ||
```tsx | ||
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> | ||
); | ||
}); | ||
``` | ||
</CodeFile> |
Oops, something went wrong.