Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(signals-preact): useLiveSignal #367

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tasty-timers-greet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@preact/signals": minor
---

Add `useLiveSignal` for instances where prop-based signal references change
32 changes: 32 additions & 0 deletions packages/preact/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,38 @@ function Counter() {
}
```

#### Signal references

When you are passing signals to components and you change the reference then the `useComputed` or `useSignalEfect`
relying on these won't notice the difference, for that we have `useLiveSignal`.

```js
import { useComputed, useLiveSignal, useSignal } from '@preact/signals';

// The signal representing props.date can change
// when i.e. someone presses a butotn
function Todo({ date }) {
const liveDateSignal = useLiveSignal(date);
const formattedDate = useComputed(() => formatDate(liveDateSignal.value.value));
return <span>{formattedDate}</span>;
}

function App() {
const dateA = useSignal(0);
const dateB = useSignal(1);
const [on, setOn] = useState(true);

return (
<main>
<button onClick={() => setDisplayed(!on)}>
Toggle
</button>
<Todo date={on ? dateA : dateB} />
</main>
);
}
```

### Rendering optimizations

The Preact adapter ships with several optimizations it can apply out of the box to skip virtual-dom rendering entirely. If you pass a signal directly into JSX, it will bind directly to the DOM `Text` node that is created and update that whenever the signal changes.
Expand Down
11 changes: 11 additions & 0 deletions packages/preact/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,17 @@ export function useSignalEffect(cb: () => void | (() => void)) {
}, []);
}

/**
* This hook is used for instances where a signal input can change across renders
* useSignal(), but rerendering with a different value arg updates the signal.
* Useful for: const a = useLiveSignal(props.a)
*/
export function useLiveSignal<T>(value: Signal<T>): Signal<Signal<T>> {
const s = useSignal(value);
if (s.peek() !== value) s.value = value;
return s;
}

/**
* @todo Determine which Reactive implementation we'll be using.
* @internal
Expand Down
Loading