@solid-hooks/core
TypeScript icon, indicating that this package has built-in type declarations

0.3.7 • Public • Published

@solid-hooks/hooks

@solid-hooks/core

useful hooks for solid.js

[!WARNING] Breaking change expected. Use at your own risk.

Install

npm i @solid-hooks/core
yarn add @solid-hooks/core
pnpm add @solid-hooks/core

Usage

createRef

read / write signal in one function

import { onMounted } from 'solid-js'
import { createRef } from '@solid-hooks/core'

function Test() {
  const divRef = createRef<HTMLDivElement>()
  return <div ref={divRef} />
}

function Counter() {
  const counter = createRef(0)
  return <button onClick={() => counter(c => c + 1)}>{counter()}</button>
}

function useSomethingRef() {
  return createRef(useSomething())
}

createReactive

make plain object props reactive

import { createReactive } from '@solid-hooks/core'

const audio = new Audio()
const [time, setCurrentTime] = createReactive(audio, 'currentTime')

createArray

create array signal

import { createArray } from '@solid-hooks/core'

const [array, setArray] = createArray(['a', 'b', 'c'])

const push = setArray(l => l.push('d'))
const pop = setArray(l => l.pop())
const reset = setArray(['a', 'b', 'c'])

createToggle

create toggle signal

import { createToggle } from '@solid-hooks/core'

const [state, toggle] = createToggle(
  false,
  value => console.log(value)
)
toggle()
toggle(true)
toggle(false)

createDirective

another way to create directive

import { createDirective } from '@solid-hooks/core'
import { type Accessor, type Setter, createRenderEffect, createSignal } from 'solid-js'

const model = createDirective((ref: Element, getter: Accessor<string>, setter: Setter<string>) => {
  createRenderEffect(() => ((ref as HTMLInputElement).value = getter()))
  ref.addEventListener('input', e => setter((e.target as HTMLInputElement | null)?.value ?? ''))
})

function TextInput() {
  const [text, setText] = createSignal('')
  return (
    <>
      <input type="text" ref={model(text, setText)} />
      <div>{text()}</div>
    </>
  )
}

reference from voby

watch

filterable and pausable createEffect(on()) like, defer by default

import { throttle } from '@solid-primitives/scheduled'
import { watch } from '@solid-hooks/core'

const [count, setCount] = createSignal(0)
const { pause, resume, isWatching, callTimes, ignoreUpdate } = watch(
  count,
  (value, oldValue, callTimes) => {
    console.log(value, oldValue, callTimes)
    const cleanup = () => { }
    return cleanup
  },
  {
    eventFilter: fn => throttle(fn, 100),
    count: 5,
    defer: false /* true by default */
  }
)

watchOnce

watch once, using createReaction

import { watchOnce } from '@solid-hooks/core'

const [count, setCount] = createSignal(0)
watchOnce(count, console.log)

watchImmediate

like watch, use createComputed

watchRendered

like watch, use createRendered

useEmits

like defineEmits in Vue, emit event from child component

import { type defineEmits, useEmits } from '@solid-hooks/core'

type Emits = defineEmits<{
  // sync
  var: number
  update: [d1: string, d2?: string, d3?: string]
  // sync or async
  fn: (test: string) => void
}>
function Child(prop: Emits & { num: number }) {
  const emit = useEmits(prop)
  const handleClick = () => {
    emit('var', { id: 1 })
    emit('update', `emit from child: ${prop.num}`, 'second param')
    emit('fn', ['a', 'b'])
  }
  return (
    <div>
      <div>
        child prop: {prop.num}
      </div>
      <button onClick={handleClick}>click and see console</button>
    </div>
  )
}

export default function Father() {
  return (
    <Child
      num={1}
      $var={e => console.log('[emit] $var:', e)}
      $update={(d, d1) => console.log(`[emit] $update:`, d, d1)}
      $fn={test => console.log('[emit] $fn:', test)}
    />
  )
}

createApp

like createApp() in Vue

import { createApp } from '@solid-hooks/core'
import App from './App'

createApp(App)
  .use(RouterProvider, { props })
  .use(I18nProvider)
  .use(GlobalStoreProvider)
  .mount('#app')

is equal to:

render(
  <RouterProvider props={props}>
    <I18nProvider>
      <GlobalStoreProvider>
        <App />
      </GlobalStoreProvider>
    </I18nProvider>
  </RouterProvider>,
  document.querySelector('#app')
)

reference from solid-utils

createContextProvider

reference from @solid-primitives/context

if default value is not defined and use context outside provider, throw Error when DEV

import { createSignal } from 'solid-js'
import { createContextProvider } from '@solid-hooks/core'

export const [TestProvider, useTestContext] = createContextProvider((param: { initial: number }) => {
  const [count, setCount] = createSignal(param.initial)
  const increment = () => setCount(count() + 1)

  return { count, increment }
})

function Child() {
  const { count, increment } = useTestContext()
  return (
    <button onClick={increment}>
      {count()}
    </button>
  )
}

export function TestContextProvider() {
  console.log('call useTestContext() outside provider:', useTestContext())
  return (
    <TestProvider initial={0}>
      <Child />
    </TestProvider>
  )
}

useCallback

create callbacks with runWithOwner, auto get current owner

reference from @solid-primitives/rootless

import { useCallback } from '@solid-hooks/core'

const handleClick = useCallback(() => {
  console.log('after 100 ms!')
})
setTimeOut(handleClick, 100)

@solid-hooks/core/web

cls

merge classes, lightweight version of clsx

import { cls } from '@solid-hooks/core/web'

cls(true && 'foo', false && 'bar', 'baz')
// => 'foo baz'

useHover

check if element is hovered

import { useHover } from '@solid-hooks/core/web'

function App() {
  let el
  const [hovered] = useHover(() => el)
  return <div ref={el}>{hovered() ? 'hovered' : 'not hovered'}</div>
}

useClickoutside

check if element is clicked outside

import { useClickoutside } from '@solid-hooks/core/web'

function App() {
  let el
  const [isClickOutside] = useClickoutside(() => el)
  return <div ref={el}>{isClickOutside() ? 'clicked outside' : 'clicked inside'}</div>
}

useLongPress

check if element is long pressed

import { useLongPress } from '@solid-hooks/core/web'

function App() {
  let el
  const [isLongPress] = useLongPress(() => el)
  return <div ref={el}>{isLongPress() ? 'long pressed' : 'not long pressed'}</div>
}

useTitle

reactive document title

import { useTitle } from '@solid-hooks/core/web'

const [title, setTitle] = useTitle()

// or with external signal
const [title, setTitle] = createSignal('')
useTitle(title)
setTitle('new title')

createObjectURL

convert blob / File / MediaSource / ArrayBuffer / ArrayBufferView / string to signal URL, auto revoke on cleanup

import { createObjectURL } from '@solid-hooks/core/web'

const [source, setMediaSource, cleanupSource] = createObjectURL(new MediaSource())
const [url, setURL, cleanupURL] = createObjectURL(new Uint8Array(8), { type: 'image/png' })

useWorkerFn

run function in worker, support local functions or external dependencies

reference from vueuse

import { createMemo, createSignal } from 'solid-js'
import { useWebWorkerFn } from '@solid-hooks/core/web'

const randomNumber = () => Math.trunc(Math.random() * 5_000_00)

function heavyTask() {
  const numbers: number[] = Array(5_000_000).fill(undefined).map(randomNumber)
  numbers.sort()
  return numbers.slice(0, 5)
}

export default function TestWorker() {
  const [fn, { status, terminate }] = useWebWorkerFn(heavyTask, { func: [randomNumber] })
  const isRunning = createMemo(() => status() === 'RUNNING')
  return (
    <>
      <div>Status: {status()}</div>
      <button onClick={() => isRunning() ? terminate() : fn().then(setData)}>
        {isRunning() ? 'terminate' : 'sort in worker'}
      </button>
    </>
  )
}

useExternal

load external CSS/JS

import { useExternal } from '@solid-hooks/core/web'

const script = 'console.log(`test load script`)'
const [scriptElement, cleanupScript] = useExternal('script', script, {/* options */})
const [styleElement, cleanupStyle] = useExternal('style', style, {/* options */})

useEventListener / useEventListenerStack / useDocumentListener / useWindowListener

auto cleanup event listener, allow nullish target

reference from @solid-primitives/event-listener

useColorMode

auto color mode with attribute toggle, disable transition by default

import { useColorMode } from '@solid-hooks/core/web'

export default function TestColorMode() {
  const [mode, setMode, isDark] = useColorMode()
  return (
    <>
      <div>{isDark() ? 'dark' : 'light'} theme</div>
      <div>{mode()}</div>
      <button onClick={() => setMode(m => m === 'dark' ? 'light' : 'dark')}>click</button>
    </>
  )
}

useNetwork

signals of network status, with onChanges callback

import { useNetwork } from '@solid-hooks/core/web'

const net = useNetwork()
const isOnline = net.online

types:

type NetworkType = 'bluetooth' | 'cellular' | 'ethernet' | 'none' | 'wifi' | 'wimax' | 'other' | 'unknown'

type EffectiveType = 'slow-2g' | '2g' | '3g' | '4g'
export type NetworkState = {
  /**
   * the time at which the connection was changed
   */
  since?: Date
  /**
   * whether the device is online
   */
  online?: boolean
  /**
   * the estimated effective round-trip time of the current connection
   */
  rtt?: number
  /**
   * type of connection a device is using to communicate with the network
   */
  type?: NetworkType
  /**
   * true if the user has set a reduced data usage option on the user agent
   */
  saveData?: boolean
  /**
   * the estimated effective bandwidth (Mb/s)
   */
  downlink?: number
  /**
   * maximum downlink speed (Mb/s)
   */
  downlinkMax?: number
  /**
   * the effective type of the connection
   */
  effectiveType?: EffectiveType
}

useIdleCallback

executes a callback using the requestIdleCallback API, fallback to setTimeout.

auto cleanup, return cleanup function.

see https://developer.mozilla.org/zh-CN/docs/Web/API/Background_Tasks_API

useCssVar

bind css variable to signal

import { useCssVar } from '@solid-hooks/core/web'

const [color, setColor] = createSignal('red')
useCssVar('bg', color)

useCopy / usePaste / createClipboardItem

hooks that paste from clipboard

import { createClipboardItem, useCopy, usePaste } from '@solid-hooks/web'

export default () => {
  const [data, setData] = createClipboardItem('test')
  const { isCopied, copy } = useCopy()

  const paste = usePaste({
    onPaste: (data, mime) => console.log(data, mime)
  })
  return (
    <>
      <div>is copied: {isCopied() ? 'true' : 'false'}</div>
      <button onClick={() => copy(data())}>copy</button>
      <button onClick={paste}>paste</button>
    </>
  )
}

License

MIT

Versions

Current Tags

VersionDownloads (Last 7 Days)Tag
0.3.76latest

Version History

VersionDownloads (Last 7 Days)Published
0.3.76
0.3.60
0.3.50
0.3.40
0.3.30
0.3.20
0.3.10
0.3.00
0.2.70
0.2.60
0.2.50
0.2.40
0.2.30
0.2.20
0.2.10
0.2.00
0.1.90
0.1.80
0.1.70
0.1.60
0.1.50
0.1.40
0.1.30
0.1.20
0.1.10
0.1.00
0.0.30

Package Sidebar

Install

npm i @solid-hooks/core

Weekly Downloads

6

Version

0.3.7

License

MIT

Unpacked Size

119 kB

Total Files

11

Last publish

Collaborators

  • subframe7536