0.4.1 • Published 20 days ago

@solid-hooks/core v0.4.1

Weekly downloads
-
License
MIT
Repository
github
Last release
20 days ago

@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 { createRef } from '@solid-hooks/core'
import { onMounted } from 'solid-js'

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())
}

createTracker

Track plain object property, make it reactive

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

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

Before v0.4.0 is createReactive

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, createRenderEffect, createSignal, type Setter } 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 { watch } from '@solid-hooks/core'
import { throttle } from '@solid-primitives/scheduled'

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 { createContextProvider } from '@solid-hooks/core'
import { createSignal } from 'solid-js'

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('foo', true && 'bar', false && ['bar', true && 'baz'], 1)
// => '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 { useWebWorkerFn } from '@solid-hooks/core/web'
import { createMemo, createSignal } from 'solid-js'

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

function heavyTask() {
  const numbers: number[] = Array.from({ length: 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

useMediaQuery

create media query signal

import { useMediaQuery, usePrefersDark } from '@solid-hooks/core/web'

const isLg = useMediaQuery('(min-width: 1024px)')
const isDark = usePrefersDark()

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

0.4.1

20 days ago

0.4.0

2 months ago

0.3.0

6 months ago

0.3.6

3 months ago

0.2.7

6 months ago

0.3.5

5 months ago

0.2.6

6 months ago

0.3.7

3 months ago

0.3.2

5 months ago

0.3.1

5 months ago

0.3.4

5 months ago

0.2.5

6 months ago

0.3.3

5 months ago

0.2.4

6 months ago

0.2.3

6 months ago

0.2.2

7 months ago

0.2.1

7 months ago

0.2.0

7 months ago

0.1.9

7 months ago

0.1.8

7 months ago

0.1.7

7 months ago

0.1.6

7 months ago

0.1.5

7 months ago

0.1.4

8 months ago

0.1.3

8 months ago

0.1.2

8 months ago

0.1.1

10 months ago

0.1.0

10 months ago

0.0.3

12 months ago