Skip to content

Commit

Permalink
Fix #107 - Export a detach utility for reactivivity
Browse files Browse the repository at this point in the history
  • Loading branch information
WebReflection committed Feb 12, 2024
1 parent 6ad4373 commit 7856ceb
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 26 deletions.
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const {
### uhtml/preactive example

```js
import { render, html, signal } from 'uhtml/preactive';
import { render, html, signal, detach } from 'uhtml/preactive';

const count = signal(0);

Expand All @@ -56,4 +56,9 @@ render(document.body, () => html`
Clicks: ${count.value}
</button>
`);
```

// stop reacting to signals in the future
setTimeout(() => {
detach(document.body);
}, 10000);
```
4 changes: 2 additions & 2 deletions esm/reactive.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Hole, html, svg, htmlFor, svgFor, attr } from './keyed.js';

import reactive from './render/reactive.js';
import { detach, attach } from './render/reactive.js';

export { Hole, reactive, html, svg, htmlFor, svgFor, attr };
export { Hole, detach, attach, html, svg, htmlFor, svgFor, attr };
6 changes: 3 additions & 3 deletions esm/reactive/preact.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export * from '@preact/signals-core';
export { Hole, html, svg, htmlFor, svgFor, attr } from '../reactive.js';
export { Hole, html, svg, htmlFor, svgFor, detach, attr } from '../reactive.js';

import { effect } from '@preact/signals-core';
import { reactive } from '../reactive.js';
export const render = reactive(effect);
import { attach } from '../reactive.js';
export const render = attach(effect);
6 changes: 3 additions & 3 deletions esm/reactive/signal.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export * from '@webreflection/signal';
export { Hole, html, svg, htmlFor, svgFor, attr } from '../reactive.js';
export { Hole, html, svg, htmlFor, svgFor, detach, attr } from '../reactive.js';

import { effect } from '@webreflection/signal';
import { reactive } from '../reactive.js';
export const render = reactive(effect);
import { attach } from '../reactive.js';
export const render = attach(effect);
46 changes: 30 additions & 16 deletions esm/render/reactive.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ const effects = new WeakMap;
*/
const onGC = dispose => dispose();

export default effect => {
let remove = true;

/**
* @param {Function} effect the reactive `effect` callback provided by a 3rd party library.
* @returns
*/
export const attach = effect => {
/**
* Render with smart updates within a generic container.
* If the `what` is a function, it automatically create
Expand All @@ -24,20 +30,28 @@ export default effect => {
* @returns {T}
*/
return (where, what) => {
let dispose = effects.get(where);
if (dispose) {
drop(dispose);
dispose();
}
if (typeof what === 'function') {
const wr = new WeakRef(where);
dispose = effect(() => { render(wr.deref(), what(), false) });
effects.set(where, dispose);
return create(dispose, onGC, { return: where });
}
else {
effects.delete(where);
return render(where, what, false);
}
remove = typeof what !== 'function';
detach(where);

if (remove) return render(where, what, false);
remove = true;

const wr = new WeakRef(where);
const dispose = effect(() => { render(wr.deref(), what(), false) });
effects.set(where, dispose);
return create(dispose, onGC, { return: where });
};
};

/**
* Allow manual cleanup of subscribed signals.
* @param {Element} where a reference container previously used to render signals.
*/
export const detach = where => {
const dispose = effects.get(where);
if (dispose) {
if (remove) effects.delete(where);
drop(dispose);
dispose();
}
};
1 change: 1 addition & 0 deletions esm/render/shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const known = new WeakMap;
* @template T
* @param {T} where the DOM node where to render content
* @param {(() => Hole) | Hole} what the hole to render
* @param {boolean} check does a `typeof` check (internal usage).
* @returns
*/
export default (where, what, check) => {
Expand Down

0 comments on commit 7856ceb

Please sign in to comment.