Skip to content

Commit b07de43

Browse files
committed
feat: Added use-interval.
1 parent 947bfde commit b07de43

File tree

7 files changed

+135
-3
lines changed

7 files changed

+135
-3
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ Based on the [@mantine/hooks](https://github.com/mantinedev/mantine/tree/master/
8383
- [x] use-in-viewport
8484
- [x] use-input-state
8585
- [x] use-intersection (Added, but note that there is [`createIntersectionObserver`](https://primitives.solidjs.community/package/intersection-observer/) in the official solid-primitives as well)
86-
- [ ] use-interval
86+
- [x] use-interval
8787
- [x] ~~use-is-first-render~~ (Every component function in SolidJS runs only once! Every component is first render only 🙂)
8888
- [x] ~~use-isomorphic-effect~~ (Solid's [`createEffect`](https://docs.solidjs.com/reference/basic-reactivity/create-effect) is technically already isomorphic because it doesn't error on SSR. Also, it also only runs on client-side.)
8989
- [ ] use-list-state
@@ -116,7 +116,7 @@ Based on the [@mantine/hooks](https://github.com/mantinedev/mantine/tree/master/
116116
- [ ] use-throttled-callback
117117
- [ ] use-throttled-state
118118
- [ ] use-throttled-value
119-
- [ ] use-timeout
119+
- [x] use-timeout
120120
- [x] use-toggle
121121
- [ ] use-uncontrolled
122122
- [ ] use-validated-state
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
```tsx
2+
import { useInterval } from 'bagon-hooks';
3+
4+
import { createSignal } from 'solid-js';
5+
6+
export function UseIntervalExample() {
7+
const [seconds, setSeconds] = createSignal(0);
8+
9+
const interval = useInterval(
10+
() => {
11+
setSeconds(s => s + 1);
12+
},
13+
1000,
14+
{ autoInvoke: true },
15+
);
16+
17+
return (
18+
<div class="flex h-full w-full flex-col items-center justify-center gap-x-3 gap-y-3 rounded-md border p-3 py-10 text-center">
19+
<p class="text-xl font-medium">Page loaded {seconds()} seconds ago.</p>
20+
<button
21+
class={`rounded-md ${
22+
!interval.active() ? 'bg-primary' : 'bg-red-500'
23+
} px-3 py-1.5 text-white transition active:scale-95`}
24+
onClick={interval.toggle}
25+
>
26+
{interval.active() ? 'Stop' : 'Start'} counting
27+
</button>
28+
</div>
29+
);
30+
}
31+
```
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { useInterval } from 'src';
2+
import { ExampleBase } from '../example-base';
3+
import Code from './use-interval.code.mdx';
4+
5+
import { createSignal } from 'solid-js';
6+
import { useMDXComponents } from 'solid-jsx';
7+
8+
export function UseIntervalExample() {
9+
const [seconds, setSeconds] = createSignal(0);
10+
11+
const interval = useInterval(
12+
() => {
13+
setSeconds(s => s + 1);
14+
},
15+
1000,
16+
{ autoInvoke: true },
17+
);
18+
19+
// @ts-ignore
20+
const components: any = useMDXComponents();
21+
22+
return (
23+
<ExampleBase
24+
title="useInterval"
25+
description="Calls function with a given interval"
26+
code={<Code components={components} />}
27+
>
28+
<div class="flex h-full w-full flex-col items-center justify-center gap-x-3 gap-y-3 rounded-md border p-3 py-10 text-center">
29+
<p class="text-xl font-medium">Page loaded {seconds()} seconds ago.</p>
30+
<button
31+
class={`rounded-md ${
32+
!interval.active() ? 'bg-primary' : 'bg-red-500'
33+
} px-3 py-1.5 text-white transition active:scale-95`}
34+
onClick={interval.toggle}
35+
>
36+
{interval.active() ? 'Stop' : 'Start'} counting
37+
</button>
38+
</div>
39+
</ExampleBase>
40+
);
41+
}

dev/constants/hooks-count.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export const HOOKS_COUNT = 36;
1+
export const HOOKS_COUNT = 38;

dev/pages/index/+Page.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { UseIdleExample } from 'dev/components/examples/use-idle/use-idle.exampl
2525
import { UseInViewportExample } from 'dev/components/examples/use-in-viewport/use-in-viewport.example';
2626
import { UseInputStateExample } from 'dev/components/examples/use-input-state/use-input-state.example';
2727
import { UseIntersectionExample } from 'dev/components/examples/use-intersection/use-intersection.example';
28+
import { UseIntervalExample } from 'dev/components/examples/use-interval/use-interval.example';
2829
import { UseKeyboardExample } from 'dev/components/examples/use-keyboard/use-keyboard.example';
2930
import { UseLocalStorageStoreExample } from 'dev/components/examples/use-local-storage-store/use-local-storage-store.example';
3031
import { UseLocalStorageExample } from 'dev/components/examples/use-local-storage/use-local-storage.example';
@@ -36,6 +37,7 @@ import { UseNetworkExample } from 'dev/components/examples/use-network/use-netwo
3637
import { UseOrientationExample } from 'dev/components/examples/use-orientation/use-orientation.example';
3738
import { UseOsExample } from 'dev/components/examples/use-os/use-os.example';
3839
import { UseResizeObserverExample } from 'dev/components/examples/use-resize-observer/use-resize-observer.example';
40+
import { UseTimeoutExample } from 'dev/components/examples/use-timeout/use-timeout.example';
3941
import { UseToggleExample } from 'dev/components/examples/use-toggle/use-toggle.example';
4042
import { Kbd } from 'dev/components/kbd';
4143
import { HOOKS_COUNT } from 'dev/constants/hooks-count';
@@ -194,6 +196,14 @@ export default function HomePage() {
194196
title: 'useDisclosureData',
195197
example: <UseDisclosureDataExample />,
196198
},
199+
{
200+
title: 'useTimeout',
201+
example: <UseTimeoutExample />,
202+
},
203+
{
204+
title: 'useInterval',
205+
example: <UseIntervalExample />,
206+
},
197207
];
198208

199209
const filteredList = createMemo(() => {

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export * from './use-idle/use-idle';
2020
export * from './use-in-viewport/use-in-viewport';
2121
export * from './use-input-state/use-input-state';
2222
export * from './use-intersection/use-intersection';
23+
export * from './use-interval/use-interval';
2324
export * from './use-local-storage/use-local-storage';
2425
export * from './use-local-storage/use-local-storage-store';
2526
export * from './use-media-query/use-media-query';
@@ -30,6 +31,7 @@ export * from './use-network/use-network';
3031
export * from './use-orientation/use-orientation';
3132
export * from './use-os/use-os';
3233
export * from './use-resize-observer/use-resize-observer';
34+
export * from './use-timeout/use-timeout';
3335
export * from './use-toggle/use-toggle';
3436

3537
export * from './utils/random-id';

src/use-interval/use-interval.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { createEffect, createSignal, onCleanup } from 'solid-js';
2+
3+
interface UseIntervalOptions {
4+
/** If set, the interval will start automatically when the component is mounted, `false` by default */
5+
autoInvoke?: boolean;
6+
}
7+
8+
export function useInterval(
9+
fn: () => void,
10+
interval: number,
11+
{ autoInvoke = false }: UseIntervalOptions = {},
12+
) {
13+
const [active, setActive] = createSignal(false);
14+
let intervalRef: number | null = null;
15+
16+
const start = () => {
17+
if (!intervalRef) {
18+
intervalRef = window.setInterval(() => {
19+
fn();
20+
}, interval);
21+
setActive(true);
22+
}
23+
};
24+
25+
const stop = () => {
26+
setActive(false);
27+
if (intervalRef) {
28+
window.clearInterval(intervalRef);
29+
intervalRef = null;
30+
}
31+
};
32+
33+
const toggle = () => {
34+
active() ? stop() : start();
35+
};
36+
37+
createEffect(() => {
38+
if (autoInvoke) {
39+
start();
40+
}
41+
42+
onCleanup(() => {
43+
stop();
44+
});
45+
});
46+
47+
return { start, stop, toggle, active };
48+
}

0 commit comments

Comments
 (0)