dy / unihooks

Universal unreacted hooks
MIT License
21 stars 1 forks source link
atomico augmentor haunted hooks preact react unihooks use use-store

unihooks experimental Build Status

Essential hooks collection for everyday react1 projects.

NPM

Principles

1. Framework agnostic
_Unihooks_ are not bound to react and work with any hooks-enabled framework: * [react](https://ghub.io/react) * [preact](https://ghub.io/preact) * [haunted](https://ghub.io/haunted) * [neverland](https://ghub.io/neverland) * [atomico](https://ghub.io/atomico) * [fuco](https://ghub.io/fuco) * [spect](https://ghub.io/spect) * [augmentor](https://ghub.io/augmentor) See [any-hooks](https://ghub.io/any-hooks) for the full list.
2. Unified
_Unihooks_ follow `useState` signature for intuitivity. ```js let [ state, actions ] = useValue( target?, init | update? ) ```
3. Essential
_Unihooks_ deliver value in reactive context, they're not mere wrappers for native API. Static hooks are avoided. ```js const MyComponent = () => { let ua = useUserAgent() } // ✘ − user agent never changes const MyComponent = () => { let ua = navigator.userAgent } // ✔ − direct API must be used instead ```

Hooks

useChannel #### `[value, setValue] = useChannel(key, init?, deps?)` Global value provider - `useState` with value identified globally by `key`. Can be used as value store, eg. as application model layer without persistency. Also can be used for intercomponent communication. `init` can be a value or a function, and (re)applies if the `key` (or `deps`) changes. ```js import { useChannel } from 'unihooks' function Component () { let [users, setUsers] = useChannel('users', { data: [], loading: false, current: null }) setUsers({ ...users, loading: true }) // or as reducer setUsers(users => { ...users, loading: false }) } ```
useStorage #### `[value, setValue] = useStorage(key, init?, options?)` `useChannel` with persistency to local/session storage. Subscribes to `storage` event - updates if storage is changed from another tab. ```js import { useStorage } from 'unihooks' function Component1 () { const [count, setCount] = useStorage('my-count', 1) } function Component2 () { const [count, setCount] = useStorage('my-count') // count === 1 setCount(2) // (↑ updates Component1 too) } function Component3 () { const [count, setCount] = useStorage('another-count', (value) => { // ...initialize value from store return value }) } ``` #### `options` * `prefix` - prefix that's added to stored keys. * `storage` - manually pass session/local/etc storage. Reference: [useStore](https://ghub.io/use-store).
useAction #### `[action] = useAction(key, cb, deps?)` Similar to `useChannel`, but used for storing functions. Different from `useChannel` in the same way the `useCallback` is different from `useMemo`. `deps` indicate if value must be reinitialized. ```js function RootComponent() { useAction('load-content', async (slug, fresh = false) => { const url = `/content/${slug}` const cache = fresh ? 'reload' : 'force-cache' const res = await fetch(url, { cache }) return await res.text() }) } function Content ({ slug = '/' }) { let [content, setContent] = useState() let [load] = useAction('load-content') useEffect(() => load().then(setContent), [slug]) return html`
${content}
` } ```
useSearchParam #### `[value, setValue] = useSearchParam(name, init?)` Reflect value to `location.search`. `value` is turned to string via [URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams). To serialize objects or arrays, provide `.toString` method or convert manually. **NOTE**. Patches `history.push` and `history.replace` to enable `pushstate` and `replacestate` events. ```js function MyComponent () { let [id, setId] = useSearchParam('id') } ```
useCountdown #### `[n, reset] = useCountdown(startValue, interval=1000 | schedule?)` Countdown value from `startValue` down to `0` with indicated `interval` in ms. Alternatively, a scheduler function can be passed as `schedule` argument, that can be eg. [worker-timers](https://ghub.io/worker-timers)-based implementation. ```js import { useCountdown } from 'unihooks' import { setInterval, clearInterval } from 'worker-timers' const Demo = () => { const [count, reset] = useCountdown(30, fn => { let id = setInterval(fn, 1000) return () => clearInterval(id) }); return `Remains: ${count}s` }; ```
useValidate #### `[error, validate] = useValidate(validator: Function | Array, init? )` Provides validation functionality. * `validator` is a function or an array of functions `value => error | true ?`. * `init` is optional initial value to validate. ```js function MyComponent () { let [usernameError, validateUsername] = useValidate([ value => !value ? 'Username is required' : true, value => value.length < 2 ? 'Username must be at least 2 chars long' : true ]) return <> validateUsername(e.target.value) && handleInputChange(e) } {...inputProps}/> { usernameError } } ```
useFormField #### `[props, field] = useFormField( options )` Form field state controller. Handles input state and validation. Useful for organizing controlled inputs or forms, a nice minimal replacement to form hooks libraries. ```js let [props, field] = useFormField({ name: 'password', type: 'password', validate: value => !!value }) // to set new input value useEffect(() => field.set(newValue)) return ``` #### `options` * `value` - initial input value. * `persist = false` - persist input state between sessions. * `validate` - custom validator for input, modifies `field.error`. See `useValidate`. * `required` - if value must not be empty. * `...props` - the rest of props is passed to `props` #### `field` * `value` - current input value. * `error` - current validation error. Revalidates on blur, `null` on focus. * `valid: bool` - is valid value, revalidates on blur. * `focus: bool` - if input is focused. * `touched: bool` - if input was focused. * `set(value)` - set input value. * `reset()` - reset form state to initial. * `validate(value)` - force-validate input.
useInput #### `[value, setValue] = useInput( element | ref )` Uncontrolled input element hook. Updates if input value changes. Setting `null` / `undefined` removes attribute from element. Useful for organizing simple input controllers, for advanced cases see [useFormField](#useFormField). ```js function MyButton() { let ref = useRef() let [value, setValue] = useInput(ref) useEffect(() => { // side-effect when value changes }, [value]) return } ```
useObservable #### `[state, setState] = useObservable(observable)` Observable as hook. Plug in any [spect/v](https://ghub.io/spect), [observable](https://ghub.io/observable), [mutant](https://ghub.io/mutant), [observ](https://ghub.io/observ) be free. ```js import { v } from 'spect/v' const vCount = v(0) function MyComponent () { let [count, setCount] = useObservable(vCount) useEffect(() => { let id = setInterval(() => setCount(count++), 1000) return () => clearInterval(id) }, []) return <>Count: { count } } ```
standard
For convenience, unihooks export current framework hooks. To switch hooks, use `setHooks` - the default export. ```js import setHooks, { useState, useEffect } from 'unihooks' import * as hooks from 'preact/hooks' setHooks(hooks) function Timer() { let [count, setCount] = useState(0) useEffect(() => { let id = setInterval(() => setCount(c => ++c)) return () => clearInterval(id) }, []) } ```
utility
Utility hooks, useful for high-order-hooks. #### `update = useUpdate()` Force-update component, regardless of internal state. #### `prev = usePrevious(value)` Returns the previous state as described in the [React hooks FAQ](https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state).

See also

Alternatives

License

MIT

HK