├── .eslintrc
├── .gitignore
├── .prettierrc
├── LICENSE
├── README.md
├── demo
├── .gitignore
├── README.md
├── index.html
├── package.json
├── src
│ ├── App.tsx
│ ├── index.tsx
│ ├── pages
│ │ ├── hooks
│ │ │ ├── useCookie.tsx
│ │ │ ├── useCounter.tsx
│ │ │ ├── useEffect.tsx
│ │ │ ├── useInterval.tsx
│ │ │ ├── useLocalStorage.tsx
│ │ │ ├── useSessionStorage.tsx
│ │ │ ├── useTimeout.tsx
│ │ │ ├── useTitle.tsx
│ │ │ ├── useToggle.tsx
│ │ │ └── useWindowSize.tsx
│ │ └── index.tsx
│ └── routes.ts
├── tsconfig.json
└── vite.config.ts
├── lerna.json
├── package.json
├── packages
└── solidjs-hooks
│ ├── .npmignore
│ ├── README.md
│ ├── package.json
│ ├── src
│ ├── hooks
│ │ ├── index.ts
│ │ ├── useBoolean.ts
│ │ ├── useCookie.ts
│ │ ├── useCounter.ts
│ │ ├── useEffect.ts
│ │ ├── useInterval.ts
│ │ ├── useLocalStorage.ts
│ │ ├── useNumber.ts
│ │ ├── useSessionStorage.ts
│ │ ├── useState.ts
│ │ ├── useTimeout.ts
│ │ ├── useTitle.ts
│ │ ├── useToggle.ts
│ │ └── useWindowSize.ts
│ ├── index.ts
│ ├── types
│ │ └── index.ts
│ └── utils
│ │ ├── helpers.ts
│ │ ├── index.ts
│ │ └── internals.ts
│ └── tsconfig.json
└── tsconfig.json
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "parser": "@typescript-eslint/parser",
4 | "extends": [
5 | "plugin:@typescript-eslint/recommended",
6 | "prettier",
7 | "prettier/@typescript-eslint",
8 | "plugin:prettier/recommended"
9 | ],
10 | "parserOptions": {
11 | "ecmaVersion": 2018,
12 | "sourceType": "module"
13 | },
14 | "rules": {
15 | "@typescript-eslint/explicit-function-return-type": "off"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | dist/
3 | yarn.lock
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "trailingComma": "es5",
4 | "semi": true,
5 | "singleQuote": true
6 | }
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022-present NukeJS
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # solidjs-hooks
2 |
3 | A collection of useful SolidJS hooks.
4 |
5 | ## Installation
6 |
7 | ```bash
8 | # Using NPM
9 | npm install solidjs-hooks
10 | # Using Yarn
11 | yarn add solidjs-hooks
12 | ```
13 |
14 | ## Usage
15 |
16 | Usage documentation can be found [here](/packages/solidjs-hooks).
--------------------------------------------------------------------------------
/demo/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
--------------------------------------------------------------------------------
/demo/README.md:
--------------------------------------------------------------------------------
1 | ## Usage
2 |
3 | Those templates dependencies are maintained via [pnpm](https://pnpm.io) via `pnpm up -Lri`.
4 |
5 | This is the reason you see a `pnpm-lock.yaml`. That being said, any package manager will work. This file can be safely be removed once you clone a template.
6 |
7 | ```bash
8 | $ npm install # or pnpm install or yarn install
9 | ```
10 |
11 | ### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs)
12 |
13 | ## Available Scripts
14 |
15 | In the project directory, you can run:
16 |
17 | ### `npm dev` or `npm start`
18 |
19 | Runs the app in the development mode.
20 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
21 |
22 | The page will reload if you make edits.
23 |
24 | ### `npm run build`
25 |
26 | Builds the app for production to the `dist` folder.
27 | It correctly bundles Solid in production mode and optimizes the build for the best performance.
28 |
29 | The build is minified and the filenames include the hashes.
30 | Your app is ready to be deployed!
31 |
32 | ## Deployment
33 |
34 | You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.)
35 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Solid App
9 |
10 |
11 | You need to enable JavaScript to run this app.
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vite-template-solid",
3 | "version": "0.0.0",
4 | "description": "",
5 | "scripts": {
6 | "start": "vite",
7 | "dev": "vite",
8 | "build": "vite build",
9 | "serve": "vite preview"
10 | },
11 | "license": "MIT",
12 | "devDependencies": {
13 | "typescript": "^4.5.5",
14 | "vite": "^2.7.13",
15 | "vite-plugin-solid": "^2.2.5"
16 | },
17 | "dependencies": {
18 | "solid-app-router": "^0.2.1",
19 | "solid-js": "^1.3.3",
20 | "solidjs-hooks": "^0.0.9"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/demo/src/App.tsx:
--------------------------------------------------------------------------------
1 | import type { Component } from 'solid-js';
2 | import { useRoutes } from 'solid-app-router';
3 |
4 | import routes from './routes';
5 |
6 | const App: Component = () => {
7 | const Routes = useRoutes(routes);
8 |
9 | return (
10 | <>
11 |
12 | >
13 | );
14 | };
15 |
16 | export default App;
17 |
--------------------------------------------------------------------------------
/demo/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { render } from 'solid-js/web';
2 |
3 | import { Router } from 'solid-app-router';
4 | import App from './App';
5 |
6 | render(
7 | () => (
8 |
9 |
10 |
11 | ),
12 | document.getElementById('root') as HTMLElement
13 | );
14 |
--------------------------------------------------------------------------------
/demo/src/pages/hooks/useCookie.tsx:
--------------------------------------------------------------------------------
1 | import type { Component } from 'solid-js';
2 | import { createSignal } from 'solid-js';
3 | import { useCookie } from 'solidjs-hooks';
4 |
5 | const HookPage: Component = () => {
6 | const [cookie, setCookie, removeCookie] = useCookie('my-cookie');
7 | const [count, setCount] = createSignal(0);
8 |
9 | const setCookieHandler = () => {
10 | setCount((prevCount) => prevCount + 1);
11 | setCookie(`my-awesome-cookie-${count()}`);
12 | };
13 |
14 | return (
15 |
16 |
Cookie value: {cookie()}
17 |
setCookieHandler()}>Update Cookie
18 |
removeCookie()}>Delete Cookie
19 |
20 | );
21 | };
22 |
23 | export default HookPage;
24 |
--------------------------------------------------------------------------------
/demo/src/pages/hooks/useCounter.tsx:
--------------------------------------------------------------------------------
1 | import type { Component } from 'solid-js';
2 | import { useCounter } from 'solidjs-hooks';
3 |
4 | const HookPage: Component = () => {
5 | const { count, increment, decrement, set, reset } = useCounter();
6 |
7 | return (
8 |
9 |
Current count: {count()}
10 |
increment()}>Increment
11 |
increment(5)}>Increment (+5)
12 |
decrement()}>Decrement
13 |
decrement(5)}>Decrement (-5)
14 |
set(100)}>Set to 100
15 |
reset()}>Reset
16 |
reset(25)}>Reset to 25
17 |
18 | );
19 | };
20 |
21 | export default HookPage;
22 |
--------------------------------------------------------------------------------
/demo/src/pages/hooks/useEffect.tsx:
--------------------------------------------------------------------------------
1 | import type { Component } from 'solid-js';
2 | import { createSignal } from 'solid-js';
3 | import { useEffect } from 'solidjs-hooks';
4 |
5 | const HookPage: Component = () => {
6 | const [count, setCount] = createSignal(0);
7 | const [delay, setDelay] = createSignal(1000);
8 |
9 | useEffect(() => {
10 | const interval = setInterval(() => {
11 | setCount((prevCount) => prevCount + 1);
12 | }, delay());
13 |
14 | return () => clearInterval(interval);
15 | });
16 |
17 | return (
18 |
19 |
Count is {count()}
20 |
setDelay((prevDelay) => prevDelay + 1000)}>
21 | Increase interval delay
22 |
23 |
setDelay((prevDelay) => prevDelay - 1000)}>
24 | Decrease interval delay
25 |
26 |
27 | );
28 | };
29 |
30 | export default HookPage;
31 |
--------------------------------------------------------------------------------
/demo/src/pages/hooks/useInterval.tsx:
--------------------------------------------------------------------------------
1 | import type { Component } from 'solid-js';
2 | import { createSignal } from 'solid-js';
3 | import { useInterval } from 'solidjs-hooks';
4 |
5 | const HookPage: Component = () => {
6 | const DEFAULT_DELAY = 1000;
7 | const [count, setCount] = createSignal(0);
8 | const [delay, setDelay] = createSignal(DEFAULT_DELAY);
9 |
10 | useInterval(() => {
11 | setCount((prevCount) => prevCount + 1);
12 | }, delay);
13 |
14 | const toggleInterval = () => {
15 | // @ts-ignore
16 | setDelay((prevDelay) => (prevDelay ? null : DEFAULT_DELAY));
17 | };
18 |
19 | return (
20 |
21 |
Count: {count()}
22 |
23 | toggleInterval()}>Toggle Interval
24 |
25 |
26 | );
27 | };
28 |
29 | export default HookPage;
30 |
--------------------------------------------------------------------------------
/demo/src/pages/hooks/useLocalStorage.tsx:
--------------------------------------------------------------------------------
1 | import type { Component } from 'solid-js';
2 | import { useLocalStorage } from 'solidjs-hooks';
3 |
4 | const HookPage: Component = () => {
5 | const [value, setValue, remove] = useLocalStorage('my-key', 'foo');
6 |
7 | return (
8 |
9 |
Value: {value()}
10 |
setValue('bar')}>bar
11 |
setValue('baz')}>baz
12 |
remove()}>Remove
13 |
14 | );
15 | };
16 |
17 | export default HookPage;
18 |
--------------------------------------------------------------------------------
/demo/src/pages/hooks/useSessionStorage.tsx:
--------------------------------------------------------------------------------
1 | import type { Component } from 'solid-js';
2 | import { useSessionStorage } from 'solidjs-hooks';
3 |
4 | const HookPage: Component = () => {
5 | const [value, setValue, remove] = useSessionStorage('my-key', 'foo');
6 |
7 | return (
8 |
9 |
Value: {value()}
10 |
setValue('bar')}>bar
11 |
setValue('baz')}>baz
12 |
remove()}>Remove
13 |
14 | );
15 | };
16 |
17 | export default HookPage;
18 |
--------------------------------------------------------------------------------
/demo/src/pages/hooks/useTimeout.tsx:
--------------------------------------------------------------------------------
1 | import type { Component } from 'solid-js';
2 | import { createSignal } from 'solid-js';
3 | import { useTimeout } from 'solidjs-hooks';
4 |
5 | const HookPage: Component = () => {
6 | const [count, setCount] = createSignal(0);
7 | const [delay, setDelay] = createSignal(1000);
8 |
9 | useTimeout(() => {
10 | setCount((prevCount) => prevCount + 1);
11 | }, delay);
12 |
13 | return (
14 |
15 |
Count is {count()}
16 |
setDelay((prevDelay) => prevDelay + 1000)}>
17 | Increase timeout delay
18 |
19 |
setDelay((prevDelay) => prevDelay - 1000)}>
20 | Decrease timeout delay
21 |
22 |
23 | );
24 | };
25 |
26 | export default HookPage;
27 |
--------------------------------------------------------------------------------
/demo/src/pages/hooks/useTitle.tsx:
--------------------------------------------------------------------------------
1 | import type { Component } from 'solid-js';
2 | import { createSignal } from 'solid-js';
3 | import { useTitle } from 'solidjs-hooks';
4 |
5 | const HookPage: Component = () => {
6 | const [title, setTitle] = createSignal(document.title);
7 |
8 | // Also accepts a string instead of an Accessor.
9 | useTitle(title);
10 |
11 | return (
12 |
13 |
Document title is: {title()}
14 |
22 |
23 | );
24 | };
25 |
26 | export default HookPage;
27 |
--------------------------------------------------------------------------------
/demo/src/pages/hooks/useToggle.tsx:
--------------------------------------------------------------------------------
1 | import type { Component } from 'solid-js';
2 | import { useToggle } from 'solidjs-hooks';
3 |
4 | const HookPage: Component = () => {
5 | const [on, toggle] = useToggle();
6 |
7 | return (
8 |
9 |
{on() ? 'ON' : 'OFF'}
10 |
toggle()}>Toggle
11 |
toggle(true)}>Turn On
12 |
toggle(false)}>Turn Off
13 |
14 | );
15 | };
16 |
17 | export default HookPage;
18 |
--------------------------------------------------------------------------------
/demo/src/pages/hooks/useWindowSize.tsx:
--------------------------------------------------------------------------------
1 | import type { Component } from 'solid-js';
2 | import { useWindowSize } from 'solidjs-hooks';
3 |
4 | const HookPage: Component = () => {
5 | const { width, height } = useWindowSize();
6 |
7 | return (
8 |
9 |
Window width: {width()}
10 |
Window height: {height()}
11 |
12 | );
13 | };
14 |
15 | export default HookPage;
16 |
--------------------------------------------------------------------------------
/demo/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import type { Component } from 'solid-js';
2 |
3 | const IndexPage: Component = () => {
4 | return (
5 | <>
6 | This is the homepage
7 | >
8 | );
9 | };
10 |
11 | export default IndexPage;
12 |
--------------------------------------------------------------------------------
/demo/src/routes.ts:
--------------------------------------------------------------------------------
1 | import type { RouteDefinition } from 'solid-app-router';
2 | import { lazy } from 'solid-js';
3 |
4 | const routes: RouteDefinition[] = [
5 | {
6 | path: '/',
7 | component: lazy(() => import('./pages/index')),
8 | },
9 |
10 | /**
11 | * Hooks Pages
12 | */
13 | {
14 | path: '/use-boolean',
15 | component: lazy(() => import('./pages/hooks/useToggle')),
16 | },
17 | {
18 | path: '/use-cookie',
19 | component: lazy(() => import('./pages/hooks/useCookie')),
20 | },
21 | {
22 | path: '/use-counter',
23 | component: lazy(() => import('./pages/hooks/useCounter')),
24 | },
25 | {
26 | path: '/use-effect',
27 | component: lazy(() => import('./pages/hooks/useEffect')),
28 | },
29 | {
30 | path: '/use-interval',
31 | component: lazy(() => import('./pages/hooks/useInterval')),
32 | },
33 | {
34 | path: '/use-local-storage',
35 | component: lazy(() => import('./pages/hooks/useLocalStorage')),
36 | },
37 | {
38 | path: '/use-session-storage',
39 | component: lazy(() => import('./pages/hooks/useSessionStorage')),
40 | },
41 | {
42 | path: '/use-timeout',
43 | component: lazy(() => import('./pages/hooks/useTimeout')),
44 | },
45 | {
46 | path: '/use-title',
47 | component: lazy(() => import('./pages/hooks/useTitle')),
48 | },
49 | {
50 | path: '/use-toggle',
51 | component: lazy(() => import('./pages/hooks/useToggle')),
52 | },
53 | {
54 | path: '/use-window-size',
55 | component: lazy(() => import('./pages/hooks/useWindowSize')),
56 | },
57 | ];
58 |
59 | export default routes;
60 |
--------------------------------------------------------------------------------
/demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "target": "ESNext",
5 | "module": "ESNext",
6 | "moduleResolution": "node",
7 | "allowSyntheticDefaultImports": true,
8 | "esModuleInterop": true,
9 | "jsx": "preserve",
10 | "jsxImportSource": "solid-js",
11 | "types": ["vite/client"]
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/demo/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import solidPlugin from 'vite-plugin-solid';
3 |
4 | export default defineConfig({
5 | plugins: [solidPlugin()],
6 | build: {
7 | target: 'esnext',
8 | polyfillDynamicImport: false,
9 | },
10 | });
11 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages": ["packages/*", "demo/*"],
3 | "version": "independent",
4 | "npmClient": "yarn",
5 | "useWorkspaces": true
6 | }
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "solidjs-hooks-workspace",
3 | "private": true,
4 | "workspaces": [
5 | "packages/*",
6 | "demo/*"
7 | ],
8 | "devDependencies": {
9 | "@types/node": "^17.0.14",
10 | "@typescript-eslint/eslint-plugin": "^5.10.2",
11 | "@typescript-eslint/parser": "^5.10.2",
12 | "eslint": "^8.8.0",
13 | "eslint-config-prettier": "^8.3.0",
14 | "eslint-plugin-prettier": "^4.0.0",
15 | "lerna": "^4.0.0",
16 | "prettier": "^2.5.1",
17 | "typescript": "^4.5.5"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | tsconfig.json
--------------------------------------------------------------------------------
/packages/solidjs-hooks/README.md:
--------------------------------------------------------------------------------
1 | # solidjs-hooks
2 |
3 | A collection of useful SolidJS hooks.
4 |
5 | ## Installation
6 |
7 | ```bash
8 | # Using NPM
9 | npm install solidjs-hooks
10 | # Using Yarn
11 | yarn add solidjs-hooks
12 | ```
13 |
14 | ## Usage
15 |
16 | ### `useBoolean`
17 |
18 | SolidJS state hook that tracks the value of a boolean.
19 |
20 | This is an alias of [`useToggle`](#usetoggle).
21 |
22 |
23 |
24 | ### `useCookie`
25 |
26 | SolidJS hook that returns the current value of a cookie, a callback to set the cookie's value, and a callback to remove the cookie.
27 |
28 | #### Usage
29 |
30 | ```jsx
31 | import { createSignal } from 'solid-js';
32 | import { useCookie } from 'solidjs-hooks';
33 |
34 | const Demo = () => {
35 | const [cookie, setCookie, removeCookie] = useCookie('my-cookie');
36 | const [count, setCount] = createSignal(0);
37 |
38 | const setCookieHandler = () => {
39 | setCount((prevCount) => prevCount + 1);
40 | setCookie(`my-awesome-cookie-${cookie()}`);
41 | }
42 |
43 | return (
44 |
45 |
Cookie value: {cookie()}
46 |
setCookieHandler()}>Update Cookie
47 |
removeCookie()}>Delete Cookie
48 |
49 | )
50 | }
51 | ```
52 |
53 |
54 |
55 | ### `useCounter`
56 |
57 | SolidJS state hook that tracks a numeric value.
58 |
59 | `useNumber` is an alias for `useCounter`.
60 |
61 | #### Usage
62 |
63 | ```jsx
64 | import { useCounter } from 'solidjs-hooks';
65 |
66 | const Demo = () => {
67 | const { count, increment, decrement, set, reset } = useCounter();
68 |
69 | return (
70 |
71 |
Current count: {count()}
72 |
increment()}>Increment
73 |
increment(5)}>Increment (+5)
74 |
decrement()}>Decrement
75 |
decrement(5)}>Decrement (-5)
76 |
set(100)}>Set to 100
77 |
reset()}>Reset
78 |
reset(25)}>Reset to 25
79 |
80 | )
81 | }
82 | ```
83 |
84 |
85 |
86 | ### `useEffect`
87 |
88 | A React-like functionality for the `createEffect` hook that SolidJS provides.
89 |
90 | #### Usage
91 |
92 | ```jsx
93 | import { createSignal } from 'solid-js';
94 | import { useEffect } from 'solidjs-hooks';
95 |
96 | const Demo = () => {
97 | const [count, setCount] = createSignal(0);
98 | const [delay, setDelay] = createSignal(1000);
99 |
100 | useEffect(() => {
101 | const interval = setInterval(() => {
102 | setCount((prevCount) => prevCount + 1);
103 | }, delay());
104 |
105 | return () => clearInterval(interval);
106 | });
107 |
108 | return (
109 |
110 |
Count is {count()}
111 |
setDelay((prevDelay) => prevDelay + 1000)}>
112 | Increase interval delay
113 |
114 |
setDelay((prevDelay) => prevDelay - 1000)}>
115 | Decrease interval delay
116 |
117 |
118 | )
119 | }
120 | ```
121 |
122 |
123 |
124 | ### `useInterval`
125 |
126 | A declarative interval hook. The interval can be paused by setting the delay to `null`.
127 |
128 | #### Usage
129 |
130 | ```jsx
131 | import { createSignal } from 'solid-js';
132 | import { useInterval } from 'solidjs-hooks';
133 |
134 | const Demo = () => {
135 | const DEFAULT_DELAY = 1000;
136 | const [count, setCount] = createSignal(0);
137 | const [delay, setDelay] = createSignal(DEFAULT_DELAY);
138 |
139 | useInterval(() => {
140 | setCount((prevCount) => prevCount + 1);
141 | }, delay);
142 |
143 | const toggleInterval = () => {
144 | setDelay((prevDelay) => prevDelay ? null : DEFAULT_DELAY);
145 | }
146 |
147 | return (
148 |
149 |
Count: {count()}
150 |
151 | toggleInterval()}>
152 | Toggle Interval
153 |
154 |
155 |
156 | )
157 | }
158 | ```
159 |
160 |
161 |
162 | ### `useLocalStorage`
163 |
164 | SolidJS side-effect hook that manages a single `localStorage` key.
165 |
166 | #### Usage
167 |
168 | ```jsx
169 | import { useLocalStorage } from 'solidjs-hooks';
170 |
171 | const Demo = () => {
172 | const [value, setValue, remove] = useLocalStorage('my-key', 'foo');
173 |
174 | return (
175 |
176 |
Value: {value()}
177 |
setValue('bar')}>bar
178 |
setValue('baz')}>baz
179 |
remove()}>Remove
180 |
181 | )
182 | }
183 | ```
184 |
185 |
186 |
187 | ### `useNumber`
188 |
189 | SolidJS state hook that tracks a numeric value.
190 |
191 | This is an alias of [`useCounter`](#usecounter).
192 |
193 |
194 |
195 | ### `useSessionStorage`
196 |
197 | SolidJS side-effect hook that manages a single `sessionStorage` key.
198 |
199 | #### Usage
200 |
201 | ```jsx
202 | import { useSessionStorage } from 'solidjs-hooks';
203 |
204 | const Demo = () => {
205 | const [value, setValue, remove] = useSessionStorage('my-key', 'foo');
206 |
207 | return (
208 |
209 |
Value: {value()}
210 |
setValue('bar')}>bar
211 |
setValue('baz')}>baz
212 |
remove()}>Remove
213 |
214 | )
215 | }
216 | ```
217 |
218 |
219 |
220 | ### `useState`
221 |
222 | This is an alias of the built-in `createSignal` hook in SolidJS.
223 |
224 |
225 |
226 | ### `useTimeout`
227 |
228 | A declarative timeout hook. When the `delay` changes, it will rerun.
229 |
230 | #### Usage
231 |
232 | ```jsx
233 | import { createSignal } from 'solid-js';
234 | import { useTimeout } from 'solidjs-hooks';
235 |
236 | const Demo = () => {
237 | const [count, setCount] = createSignal(0);
238 | const [delay, setDelay] = createSignal(1000);
239 |
240 | useTimeout(() => {
241 | setCount((prevCount) => prevCount + 1);
242 | }, delay);
243 |
244 | return (
245 |
246 |
Count is {count()}
247 |
setDelay((prevDelay) => prevDelay + 1000)}>
248 | Increase timeout delay
249 |
250 |
setDelay((prevDelay) => prevDelay - 1000)}>
251 | Decrease timeout delay
252 |
253 |
254 | )
255 | }
256 | ```
257 |
258 |
259 |
260 | ### `useTitle`
261 |
262 | SolidJS side-effect hook that sets the title of the page.
263 |
264 | #### Usage
265 |
266 | ```jsx
267 | import { createSignal } from 'solid-js';
268 | import { useTitle } from 'solidjs-hooks';
269 |
270 | const Demo = () => {
271 | const [title, setTitle] = createSignal(document.title);
272 |
273 | // Also accepts a string instead of an Accessor.
274 | useTitle(title);
275 |
276 | return (
277 |
278 |
Document title is: {title()}
279 |
283 |
284 | )
285 | }
286 | ```
287 |
288 |
289 |
290 | ### `useToggle`
291 |
292 | SolidJS state hook that tracks the value of a boolean.
293 |
294 | `useBoolean` is an alias for `useToggle`.
295 |
296 | #### Usage
297 |
298 | ```jsx
299 | import { useToggle } from 'solidjs-hooks';
300 |
301 | const Demo = () => {
302 | const [on, toggle] = useToggle();
303 |
304 | return (
305 |
306 |
{on() ? "ON" : "OFF"}
307 |
toggle()}>Toggle
308 |
toggle(true)}>Turn On
309 |
toggle(false)}>Turn Off
310 |
311 | )
312 | }
313 | ```
314 |
315 |
316 |
317 | ### `useWindowSize`
318 |
319 | SolidJS sensor hook that tracks dimensions of the browser window.
320 |
321 | #### Usage
322 |
323 | ```jsx
324 | import { useWindowSize } from 'solidjs-hooks';
325 |
326 | const Demo = () => {
327 | const { width, height } = useWindowSize();
328 |
329 | return (
330 |
331 |
Window width: {width()}
332 |
Window height: {height()}
333 |
334 | )
335 | }
336 | ```
--------------------------------------------------------------------------------
/packages/solidjs-hooks/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "solidjs-hooks",
3 | "description": "A collection of useful SolidJS hooks.",
4 | "private": false,
5 | "version": "1.1.2",
6 | "license": "MIT",
7 | "scripts": {
8 | "build": "pridepack clean && pridepack build"
9 | },
10 | "author": {
11 | "name": "NukeJS",
12 | "url": "https://github.com/NukeJS"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git+https://github.com/NukeJS/solidjs-hooks"
17 | },
18 | "readme": "https://github.com/NukeJS/solidjs-hooks/tree/master/packages/solidjs-hooks#readme",
19 | "bugs": {
20 | "url": "https://github.com/NukeJS/solidjs-hooks/issues"
21 | },
22 | "keywords": [
23 | "solid-js",
24 | "hooks",
25 | "use"
26 | ],
27 | "types": "dist/types/index.d.ts",
28 | "main": "dist/cjs/production/index.js",
29 | "module": "dist/esm/production/index.js",
30 | "exports": {
31 | ".": {
32 | "development": {
33 | "require": "./dist/cjs/development/index.js",
34 | "import": "./dist/esm/development/index.js"
35 | },
36 | "require": "./dist/cjs/production/index.js",
37 | "import": "./dist/esm/production/index.js",
38 | "types": "./dist/types/index.d.ts"
39 | },
40 | "./dev": {
41 | "production": {
42 | "require": "./dist/cjs/production/index.js",
43 | "import": "./dist/esm/production/index.js"
44 | },
45 | "require": "./dist/cjs/development/index.js",
46 | "import": "./dist/esm/development/index.js",
47 | "types": "./dist/types/index.d.ts"
48 | },
49 | "./esm": {
50 | "development": "./dist/esm/development/index.js",
51 | "production": "./dist/esm/production/index.js",
52 | "default": "./dist/esm/production/index.js",
53 | "types": "./dist/types/index.d.ts"
54 | },
55 | "./cjs": {
56 | "development": "./dist/cjs/development/index.js",
57 | "production": "./dist/cjs/production/index.js",
58 | "default": "./dist/cjs/production/index.js",
59 | "types": "./dist/types/index.d.ts"
60 | }
61 | },
62 | "files": [
63 | "dist"
64 | ],
65 | "devDependencies": {
66 | "@types/js-cookie": "^3.0.1",
67 | "@types/node": "^17.0.14",
68 | "pridepack": "1.0.3",
69 | "solid-js": "^1.3.5",
70 | "typescript": "^4.5.5"
71 | },
72 | "dependencies": {
73 | "js-cookie": "^3.0.1"
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/hooks/index.ts:
--------------------------------------------------------------------------------
1 | export { default as useBoolean } from './useBoolean';
2 | export { default as useCounter } from './useCounter';
3 | export { default as useCookie } from './useCookie';
4 | export { default as useEffect } from './useEffect';
5 | export { default as useInterval } from './useInterval';
6 | export { default as useLocalStorage } from './useLocalStorage';
7 | export { default as useNumber } from './useNumber';
8 | export { default as useSessionStorage } from './useSessionStorage';
9 | export { default as useState } from './useState';
10 | export { default as useTimeout } from './useTimeout';
11 | export { default as useTitle } from './useTitle';
12 | export { default as useToggle } from './useToggle';
13 | export { default as useWindowSize } from './useWindowSize';
14 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/hooks/useBoolean.ts:
--------------------------------------------------------------------------------
1 | import useBoolean from './useToggle';
2 |
3 | export default useBoolean;
4 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/hooks/useCookie.ts:
--------------------------------------------------------------------------------
1 | import type { Accessor } from 'solid-js';
2 | import { createSignal } from 'solid-js';
3 | import Cookies from 'js-cookie';
4 |
5 | function useCookie(
6 | key: string
7 | ): [
8 | Accessor,
9 | (newValue: string, options?: Cookies.CookieAttributes) => void,
10 | () => void
11 | ] {
12 | const [cookie, setCookie] = createSignal(Cookies.get(key) || null);
13 |
14 | const set = (newValue: string, options?: Cookies.CookieAttributes) => {
15 | Cookies.set(key, newValue, options);
16 | setCookie(newValue);
17 | };
18 |
19 | const remove = () => {
20 | Cookies.remove(key);
21 | setCookie(null);
22 | };
23 |
24 | return [cookie, set, remove];
25 | }
26 |
27 | export default useCookie;
28 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/hooks/useCounter.ts:
--------------------------------------------------------------------------------
1 | import type { Accessor } from 'solid-js';
2 | import { createSignal } from 'solid-js';
3 |
4 | interface CounterActions {
5 | count: Accessor;
6 | increment: (amount?: number) => void;
7 | decrement: (amount?: number) => void;
8 | set: (amount: number) => void;
9 | reset: (amount?: number) => void;
10 | }
11 |
12 | function useCounter(initialValue: number = 0): CounterActions {
13 | const [count, setCount] = createSignal(initialValue);
14 |
15 | const increment = (amount: number = 1) => {
16 | if (typeof amount !== 'number') {
17 | throw new Error(`amount has to be a number, got "${typeof amount}".`);
18 | }
19 |
20 | setCount((prevCount) => prevCount + amount);
21 | };
22 |
23 | const decrement = (amount: number = 1) => {
24 | if (typeof amount !== 'number') {
25 | throw new Error(`amount has to be a number, got "${typeof amount}".`);
26 | }
27 |
28 | setCount((prevCount) => prevCount - amount);
29 | };
30 |
31 | const set = (amount: number) => {
32 | if (typeof amount !== 'number') {
33 | throw new Error(`amount has to be a number, got "${typeof amount}".`);
34 | }
35 |
36 | setCount(amount);
37 | };
38 |
39 | const reset = (amount: number = initialValue) => {
40 | set(amount);
41 | };
42 |
43 | return {
44 | count,
45 | increment,
46 | decrement,
47 | set,
48 | reset,
49 | };
50 | }
51 |
52 | export default useCounter;
53 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/hooks/useEffect.ts:
--------------------------------------------------------------------------------
1 | import { createEffect, onCleanup } from 'solid-js';
2 | import { isDefined, isFunction } from '../utils';
3 |
4 | function useEffect(callback: () => void | Function): void {
5 | createEffect(() => {
6 | if (isDefined(callback) && isFunction(callback)) {
7 | const cleanup = callback();
8 | if (isFunction(cleanup)) {
9 | onCleanup(() => cleanup());
10 | }
11 | }
12 |
13 | return;
14 | });
15 | }
16 |
17 | export default useEffect;
18 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/hooks/useInterval.ts:
--------------------------------------------------------------------------------
1 | import type { MaybeAccessor } from '../types';
2 | import { useEffect } from './';
3 | import { access, isDefined } from '../utils';
4 |
5 | function useInterval(
6 | callback: () => void,
7 | delay?: MaybeAccessor | null
8 | ): void {
9 | useEffect(() => {
10 | const _delay = access(delay);
11 | if (!isDefined(_delay) || typeof _delay !== 'number' || _delay < 0) return;
12 |
13 | const interval = setInterval(callback, _delay);
14 |
15 | return () => clearInterval(interval);
16 | });
17 | }
18 |
19 | export default useInterval;
20 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/hooks/useLocalStorage.ts:
--------------------------------------------------------------------------------
1 | import type { Accessor } from 'solid-js';
2 | import { createRenderEffect, createSignal } from 'solid-js';
3 |
4 | interface LocalStorageOptions {
5 | serializer?: (value: T) => string;
6 | deserializer?: (value: string) => T;
7 | }
8 |
9 | function useLocalStorage(
10 | key: string,
11 | initialValue: T,
12 | options?: LocalStorageOptions
13 | ): [Accessor, (value: T) => void, () => void];
14 | function useLocalStorage(
15 | key: string,
16 | initialValue?: T,
17 | options?: LocalStorageOptions
18 | ): [Accessor, (value: T) => void, () => void];
19 | function useLocalStorage(
20 | key: string,
21 | initialValue?: T,
22 | options?: LocalStorageOptions
23 | ): [Accessor, (value: T) => void, () => void] {
24 | const serializer = (value: T) => {
25 | if (options?.serializer) {
26 | return options.serializer(value);
27 | }
28 | return JSON.stringify(value);
29 | };
30 |
31 | const deserializer = (value: string) => {
32 | if (options?.deserializer) {
33 | return options.deserializer(value);
34 | }
35 |
36 | try {
37 | return JSON.parse(value);
38 | } catch {
39 | return value;
40 | }
41 | };
42 |
43 | const getStoredValue = () => {
44 | try {
45 | const raw = localStorage.getItem(key);
46 | if (raw) {
47 | return deserializer(raw);
48 | }
49 | } catch (error) {
50 | console.error(error);
51 | }
52 |
53 | return initialValue;
54 | };
55 |
56 | const [state, setState] = createSignal(getStoredValue());
57 |
58 | const set = (value: T) => {
59 | try {
60 | localStorage.setItem(key, serializer(value));
61 | setState(() => value);
62 | } catch (error) {
63 | console.error(error);
64 | }
65 | };
66 |
67 | const remove = () => {
68 | try {
69 | localStorage.removeItem(key);
70 | setState(undefined);
71 | } catch (error) {
72 | console.error(error);
73 | }
74 | };
75 |
76 | createRenderEffect(() => {
77 | set(getStoredValue());
78 | });
79 |
80 | return [state, set, remove];
81 | }
82 |
83 | export default useLocalStorage;
84 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/hooks/useNumber.ts:
--------------------------------------------------------------------------------
1 | import useNumber from './useCounter';
2 |
3 | export default useNumber;
4 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/hooks/useSessionStorage.ts:
--------------------------------------------------------------------------------
1 | import type { Accessor } from 'solid-js';
2 | import { createRenderEffect, createSignal } from 'solid-js';
3 |
4 | interface SessionStorageOptions {
5 | serializer?: (value: T) => string;
6 | deserializer?: (value: string) => T;
7 | }
8 |
9 | function useSessionStorage(
10 | key: string,
11 | initialValue: T,
12 | options?: SessionStorageOptions
13 | ): [Accessor, (value: T) => void, () => void];
14 | function useSessionStorage(
15 | key: string,
16 | initialValue?: T,
17 | options?: SessionStorageOptions
18 | ): [Accessor, (value: T) => void, () => void];
19 | function useSessionStorage(
20 | key: string,
21 | initialValue?: T,
22 | options?: SessionStorageOptions
23 | ): [Accessor, (value: T) => void, () => void] {
24 | const serializer = (value: T) => {
25 | if (options?.serializer) {
26 | return options.serializer(value);
27 | }
28 | return JSON.stringify(value);
29 | };
30 |
31 | const deserializer = (value: string) => {
32 | if (options?.deserializer) {
33 | return options.deserializer(value);
34 | }
35 |
36 | try {
37 | return JSON.parse(value);
38 | } catch {
39 | return value;
40 | }
41 | };
42 |
43 | const getStoredValue = () => {
44 | try {
45 | const raw = sessionStorage.getItem(key);
46 | if (raw) {
47 | return deserializer(raw);
48 | }
49 | } catch (error) {
50 | console.error(error);
51 | }
52 |
53 | return initialValue;
54 | };
55 |
56 | const [state, setState] = createSignal(getStoredValue());
57 |
58 | const set = (value: T) => {
59 | try {
60 | sessionStorage.setItem(key, serializer(value));
61 | setState(() => value);
62 | } catch (error) {
63 | console.error(error);
64 | }
65 | };
66 |
67 | const remove = () => {
68 | try {
69 | sessionStorage.removeItem(key);
70 | setState(undefined);
71 | } catch (error) {
72 | console.error(error);
73 | }
74 | };
75 |
76 | createRenderEffect(() => {
77 | set(getStoredValue());
78 | });
79 |
80 | return [state, set, remove];
81 | }
82 |
83 | export default useSessionStorage;
84 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/hooks/useState.ts:
--------------------------------------------------------------------------------
1 | import { createSignal as useState } from 'solid-js';
2 |
3 | export default useState;
4 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/hooks/useTimeout.ts:
--------------------------------------------------------------------------------
1 | import type { MaybeAccessor } from '../types';
2 | import { useEffect } from './';
3 | import { access, isDefined } from '../utils';
4 |
5 | function useTimeout(
6 | callback: () => void,
7 | delay?: MaybeAccessor | null
8 | ): void {
9 | useEffect(() => {
10 | const _delay = access(delay);
11 | if (!isDefined(_delay) || typeof _delay !== 'number' || _delay < 0) return;
12 |
13 | const timeout = setTimeout(callback, _delay);
14 |
15 | return () => clearTimeout(timeout);
16 | });
17 | }
18 |
19 | export default useTimeout;
20 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/hooks/useTitle.ts:
--------------------------------------------------------------------------------
1 | import type { MaybeAccessor } from '../types';
2 | import { createEffect, onCleanup } from 'solid-js';
3 | import { isBrowser, access } from '../utils';
4 |
5 | export interface TitleOptions {
6 | restoreOnCleanup?: boolean;
7 | }
8 |
9 | const DEFAULT_OPTIONS: TitleOptions = {
10 | restoreOnCleanup: false,
11 | };
12 |
13 | function useTitle(
14 | title: MaybeAccessor,
15 | options: TitleOptions = DEFAULT_OPTIONS
16 | ): void {
17 | const _title = isBrowser ? document.title : '';
18 |
19 | createEffect(() => {
20 | document.title = access(title);
21 | });
22 |
23 | onCleanup(() => {
24 | if (options.restoreOnCleanup) {
25 | document.title = _title;
26 | }
27 | });
28 | }
29 |
30 | export default useTitle;
31 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/hooks/useToggle.ts:
--------------------------------------------------------------------------------
1 | import type { Accessor } from 'solid-js';
2 | import { createSignal } from 'solid-js';
3 |
4 | function useToggle(
5 | initialValue: boolean = false
6 | ): [Accessor, (newValue?: boolean) => void] {
7 | const [value, setValue] = createSignal(initialValue);
8 |
9 | const toggle = (newValue?: boolean) => {
10 | setValue((prevValue) =>
11 | typeof newValue === 'boolean' ? newValue : !prevValue
12 | );
13 | };
14 |
15 | return [value, toggle];
16 | }
17 |
18 | export default useToggle;
19 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/hooks/useWindowSize.ts:
--------------------------------------------------------------------------------
1 | import type { Accessor } from 'solid-js';
2 | import { createSignal, createRenderEffect } from 'solid-js';
3 | import { useEffect } from './';
4 |
5 | function useWindowSize(): {
6 | width: Accessor;
7 | height: Accessor;
8 | } {
9 | const [width, setWidth] = createSignal(0);
10 | const [height, setHeight] = createSignal(0);
11 |
12 | const handler = () => {
13 | setWidth(window.innerWidth);
14 | setHeight(window.innerHeight);
15 | };
16 |
17 | useEffect(() => {
18 | window.addEventListener('resize', handler);
19 |
20 | return () => window.removeEventListener('resize', handler);
21 | });
22 |
23 | createRenderEffect(() => {
24 | handler();
25 | });
26 |
27 | return { width, height };
28 | }
29 |
30 | export default useWindowSize;
31 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './hooks';
2 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/types/index.ts:
--------------------------------------------------------------------------------
1 | import type { Accessor } from 'solid-js';
2 |
3 | export type Fn = () => R;
4 |
5 | export type MaybeAccessor = T | Accessor;
6 | export type MaybeAccessorValue> = T extends Fn
7 | ? ReturnType
8 | : T;
9 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/utils/helpers.ts:
--------------------------------------------------------------------------------
1 | export function isDefined(value: T | undefined | null): value is T {
2 | return typeof value !== 'undefined' && value !== null;
3 | }
4 |
5 | export function isFunction(value: T | Function): value is Function {
6 | return typeof value === 'function';
7 | }
8 |
9 | export const isBrowser = typeof window !== 'undefined';
10 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './helpers';
2 | export * from './internals';
3 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/src/utils/internals.ts:
--------------------------------------------------------------------------------
1 | import type { MaybeAccessor, MaybeAccessorValue } from '../types';
2 | import { isFunction } from './';
3 |
4 | export function access>(
5 | value: T
6 | ): MaybeAccessorValue {
7 | return isFunction(value) ? value() : value;
8 | }
9 |
--------------------------------------------------------------------------------
/packages/solidjs-hooks/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "lib": ["DOM"],
5 | "declaration": true,
6 | "esModuleInterop": true,
7 | "jsx": "react"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["ESNext"],
4 | "target": "ESNext",
5 | "module": "CommonJS",
6 | "strict": true,
7 | "declaration": true,
8 | "esModuleInterop": true,
9 | "skipLibCheck": true,
10 | "resolveJsonModule": true,
11 | "downlevelIteration": true
12 | }
13 | }
14 |
--------------------------------------------------------------------------------