Skip to content

Commit e7cecab

Browse files
committed
feat: Added use-debounced-callback, use-debounced-signal, use-debounced-value, use-did-update, use-document-visibility, use-in-viewport, use-input-state, use-intersection.
1 parent 1673bd6 commit e7cecab

30 files changed

+729
-17
lines changed

README.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,21 +53,21 @@ We want to achieve as 1:1 as possible with Mantine's original hooks. So you can
5353

5454
Based on the [@mantine/hooks](https://github.com/mantinedev/mantine/tree/master/packages/%40mantine/hooks/src) library.
5555

56-
- [ ] use-callback-ref
56+
- [x] ~~use-callback-ref~~ (Not needed, only used internally by mantine for preventing re-renders on a function)
5757
- [x] use-click-outside
5858
- [x] use-clipboard
5959
- [ ] use-color-scheme
6060
- [x] use-counter
61-
- [ ] use-debounced-callback
62-
- [ ] ~~use-debounced-state~~ use-debounced-signal
63-
- [ ] use-debounced-value
64-
- [ ] use-did-update
61+
- [x] use-debounced-callback
62+
- [x] ~~use-debounced-state~~ use-debounced-signal
63+
- [x] use-debounced-value
64+
- [x] use-did-update
6565
- [ ] use-disclosure
6666
- [ ] use-document-title
67-
- [ ] use-document-visibility
67+
- [x] use-document-visibility
6868
- [ ] use-event-listener
69-
- [x] use-eye-dropper (improved, state management is inside the hook)
70-
- [x] use-favicon (improved, more flexible)
69+
- [x] use-eye-dropper (improved, state management is inside the hook)
70+
- [x] use-favicon (improved, more flexible, better control)
7171
- [ ] use-fetch
7272
- [ ] use-focus-return
7373
- [ ] use-focus-trap
@@ -79,13 +79,13 @@ Based on the [@mantine/hooks](https://github.com/mantinedev/mantine/tree/master/
7979
- [x] use-hotkeys
8080
- [x] use-hover
8181
- [x] use-id (Added, but note that there is [`createUniqueId`](https://docs.solidjs.com/reference/component-apis/create-unique-id) in Solid)
82-
- [x] use-idle (Added, but note that there is [`createIdleTimer`](https://primitives.solidjs.community/package/idle/) solid-primitives as well)
83-
- [ ] use-in-viewport
84-
- [ ] use-input-state
85-
- [ ] use-intersection
82+
- [x] use-idle (Added, but note that there is [`createIdleTimer`](https://primitives.solidjs.community/package/idle/) the official solid-primitives as well)
83+
- [x] use-in-viewport
84+
- [x] use-input-state
85+
- [x] use-intersection (Added, but note that there is [`createIntersectionObserver`](https://primitives.solidjs.community/package/intersection-observer/) in the official solid-primitives as well)
8686
- [ ] use-interval
87-
- [ ] use-is-first-render
88-
- [x] ~~use-isomorphic-effect~~ (Solid's [`createEffect`](https://docs.solidjs.com/reference/basic-reactivity/create-effect) is actually isomorphic - it works in browser and server).
87+
- [x] ~~use-is-first-render~~ (Every component function in SolidJS runs only once! Every component is first render only 🙂)
88+
- [x] use-isomorphic-effect (Solid's [`createEffect`](https://docs.solidjs.com/reference/basic-reactivity/create-effect) is actually isomorphic - it works in browser and server).
8989
- [ ] use-list-state
9090
- [x] use-local-storage
9191
- [ ] use-logger

dev/components/examples/example-base.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export function ExampleBase(props: FlowProps<ExampleBaseProps>) {
1212
const [viewing, setViewing] = createSignal<'code' | 'result'>('result');
1313

1414
return (
15-
<div class="flex h-full max-h-[500px] w-full flex-col items-start gap-y-3 overflow-hidden rounded-xl border bg-white p-5">
15+
<div class="flex h-full max-h-[500px] w-full flex-col items-start gap-y-3 overflow-hidden rounded-lg bg-white p-5">
1616
<div class="flex w-full items-center justify-between">
1717
<h2 class="text-xl font-bold">{props.title}</h2>
1818
<button
@@ -25,7 +25,7 @@ export function ExampleBase(props: FlowProps<ExampleBaseProps>) {
2525
</button>
2626
</div>
2727

28-
<p class="text-sm text-opacity-70">{props.description}</p>
28+
<div class="text-sm text-opacity-70">{props.description}</div>
2929

3030
<Show when={viewing() === 'result'}>
3131
<div class="w-full flex-1 rounded-md">{props.children}</div>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
```tsx
2+
const [search, setSearch] = createSignal('');
3+
const [searchResults, setSearchResults] = createSignal<{ id: number; title: string }[]>([]);
4+
const [loading, setLoading] = createSignal(false);
5+
6+
const debouncedSearch = useDebouncedCallback(async (query: string) => {
7+
setLoading(true);
8+
setSearchResults(await getSearchResults(query));
9+
setLoading(false);
10+
}, 500);
11+
12+
const handleInput: JSX.EventHandler<HTMLInputElement, InputEvent> = event => {
13+
setSearch(event.currentTarget.value);
14+
debouncedSearch(event.currentTarget.value);
15+
};
16+
17+
return (
18+
<div>
19+
<input type="text" value={search} onInput={handleInput} />
20+
<Show when={loading()}
21+
children={<div>Loading...</div>}
22+
fallback={<For each={searchResults()}>{result => <div>{result.title}</div>}</For>}
23+
/>
24+
</div>;
25+
)
26+
```
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { useDebouncedCallback } from 'src';
2+
import { ExampleBase } from '../example-base';
3+
import Code from './use-debounced-callback.code.mdx';
4+
5+
import { createSignal, For, JSX, Show } from 'solid-js';
6+
import { useMDXComponents } from 'solid-jsx';
7+
8+
function getSearchResults(query: string): Promise<{ id: number; title: string }[]> {
9+
return new Promise(resolve => {
10+
setTimeout(() => {
11+
resolve(
12+
query.trim() === ''
13+
? []
14+
: Array(5)
15+
.fill(0)
16+
.map((_, index) => ({ id: index, title: `${query} ${index + 1}` })),
17+
);
18+
}, 1000);
19+
});
20+
}
21+
22+
export function UseDebouncedCallbackExample() {
23+
const [search, setSearch] = createSignal('');
24+
const [searchResults, setSearchResults] = createSignal<{ id: number; title: string }[]>([]);
25+
const [loading, setLoading] = createSignal(false);
26+
27+
const debouncedSearch = useDebouncedCallback(async (query: string) => {
28+
setLoading(true);
29+
setSearchResults(await getSearchResults(query));
30+
setLoading(false);
31+
}, 500);
32+
33+
const handleInput: JSX.EventHandler<HTMLInputElement, InputEvent> = event => {
34+
setSearch(event.currentTarget.value);
35+
debouncedSearch(event.currentTarget.value);
36+
};
37+
38+
// @ts-ignore
39+
const components: any = useMDXComponents();
40+
41+
return (
42+
<ExampleBase
43+
title="useDebouncedCallback"
44+
description="Creates a debounced version of a callback function, delaying its execution until a specified time has elapsed since the last invocation."
45+
code={<Code components={components} />}
46+
>
47+
<div class="flex h-full w-full flex-col items-center justify-center gap-x-1 gap-y-2 rounded-md border p-3 py-10 text-center text-sm">
48+
<input
49+
value={search()}
50+
onInput={handleInput}
51+
class="rounded-md border p-2"
52+
placeholder="Search..."
53+
/>
54+
55+
<Show
56+
when={loading()}
57+
children={<>Loading...</>}
58+
fallback={
59+
<For each={searchResults()}>{result => <div class="text-xs">{result.title}</div>}</For>
60+
}
61+
/>
62+
</div>
63+
</ExampleBase>
64+
);
65+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
```tsx
2+
const [signal, setSignal] = useDebouncedSignal('', 500);
3+
return (
4+
<>
5+
<input value={signal()} onInput={e => setSignal(e.currentTarget.value)} />
6+
<span>State: {JSON.stringify(signal())}</span>
7+
</>
8+
);
9+
```
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useDebouncedSignal } from 'src';
2+
import { ExampleBase } from '../example-base';
3+
import Code from './use-debounced-signal.code.mdx';
4+
5+
import { useMDXComponents } from 'solid-jsx';
6+
7+
export function UseDebouncedSignalExample() {
8+
const [signal, setSignal] = useDebouncedSignal('', 500);
9+
10+
// @ts-ignore
11+
const components: any = useMDXComponents();
12+
13+
return (
14+
<ExampleBase
15+
title="useDebouncedSignal"
16+
description="Creates a signal that is debounced with a given wait time."
17+
code={<Code components={components} />}
18+
>
19+
<div class="flex h-full w-full flex-col items-center justify-center gap-x-1 gap-y-2 rounded-md border p-3 py-10 text-center text-sm">
20+
<input
21+
value={signal()}
22+
onInput={e => setSignal(e.currentTarget.value)}
23+
class="rounded-md border p-2"
24+
/>
25+
<span>State: {JSON.stringify(signal())}</span>
26+
</div>
27+
</ExampleBase>
28+
);
29+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
```tsx
2+
const id = useId();
3+
4+
return (
5+
<div>
6+
Random ID: {id()}
7+
</div>;
8+
)
9+
```
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { useDebouncedValue } from 'src';
2+
import { ExampleBase } from '../example-base';
3+
import Code from './use-debounced-value.code.mdx';
4+
5+
import { createSignal } from 'solid-js';
6+
import { useMDXComponents } from 'solid-jsx';
7+
8+
export function UseDebouncedValueExample() {
9+
const [signal, setSignal] = createSignal('');
10+
const [value, cancel] = useDebouncedValue(signal, 500);
11+
12+
// @ts-ignore
13+
const components: any = useMDXComponents();
14+
15+
return (
16+
<ExampleBase
17+
title="useDebouncedValue"
18+
description="Debounced value from an existing signal."
19+
code={<Code components={components} />}
20+
>
21+
<div class="flex h-full w-full flex-col items-center justify-center gap-x-1 gap-y-2 rounded-md border p-3 py-10 text-center text-sm">
22+
<input
23+
value={signal()}
24+
onInput={e => setSignal(e.currentTarget.value)}
25+
class="rounded-md border p-2"
26+
/>
27+
<div class="flex items-center gap-x-2">
28+
<span>State: {JSON.stringify(signal())}</span>
29+
<span>|</span>
30+
<span>Value: {JSON.stringify(value())}</span>
31+
</div>
32+
</div>
33+
</ExampleBase>
34+
);
35+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
```tsx
2+
const [signal, setSignal] = createSignal(0);
3+
4+
useDidUpdate(() => {
5+
console.log('Did Update', signal());
6+
}, signal);
7+
8+
return (
9+
<div>
10+
<button onClick={setSignal(prev => prev + 1)}>Simulate Update {signal()}</button>
11+
</div>;
12+
)
13+
```
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { useDidUpdate } from 'src';
2+
import { ExampleBase } from '../example-base';
3+
import Code from './use-did-update.code.mdx';
4+
5+
import { createSignal } from 'solid-js';
6+
import { useMDXComponents } from 'solid-jsx';
7+
8+
export function UseDidUpdateExample() {
9+
const [signal, setSignal] = createSignal(0);
10+
11+
useDidUpdate(() => {
12+
console.log('Did Update', signal());
13+
}, signal);
14+
15+
// @ts-ignore
16+
const components: any = useMDXComponents();
17+
18+
return (
19+
<ExampleBase
20+
title="useDidUpdate"
21+
description={
22+
<>
23+
This hook works the same way as createEffect but it is not called when component is
24+
mounted.
25+
<br />
26+
<br />
27+
Unlike createEffect, this always has a dependency of signals (like React's useEffect) for
28+
the reason that the only way to do this in Solid is using the on() + defer property under
29+
the hood.
30+
</>
31+
}
32+
code={<Code components={components} />}
33+
>
34+
<div class="flex h-full w-full flex-col items-center justify-center gap-x-1 gap-y-2 rounded-md border p-3 py-10 text-center text-sm">
35+
<p class="max-w-xs text-center text-xs">
36+
This logs "Did Update {signal() === 0 ? 'X' : signal()}" to the console. Notice that it
37+
doesn't log "Did Update 0" since it happens on mount.
38+
</p>
39+
<button
40+
class="rounded-md bg-primary p-2 text-white transition active:scale-95"
41+
onClick={() => {
42+
setSignal(signal() + 1);
43+
}}
44+
>
45+
Simulate an Update {signal()}
46+
</button>
47+
</div>
48+
</ExampleBase>
49+
);
50+
}

0 commit comments

Comments
 (0)