Essential hooks collection for everyday react1 projects.
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
any-hooks - cross-framework standard hooks provider.