npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@saramorillon/hooks

v2.12.4

Published

Some utility hooks

Downloads

7

Readme

Hooks

Some utility hooks.

Table of contents

useTheme

useTheme returns 'dark' or 'light' according to prefers-color-scheme media query.

It also changes document root color-scheme accordingly.

Example

import { useTheme } from '@saramorillon/hooks'

function MyComponent() {
  const theme = useTheme()
  return theme === 'dark' ? <Dark /> : <Light />
}

API

useTheme(): Theme

Arguments

None

Returns

Theme - The theme ("light" or "dark").

useQuery

useQuery provides an easy way to make any action that triggers a loading, may return some data and may generate an error (typical API call use case).

Example

import { useQuery } from '@saramorillon/hooks'
import { getData } from './service'

function MyComponent() {
  const { execute, loading, error, result } = useQuery(getData)

  if (loading) return <Spinner />
  if (error) return <Error />
  return (
    <>
      <Button onClick={execute}>Load data</Button>
      <Table data={result} />
    </>
  )
}

API

useQuery<T>(queryFn: () => Promise<T>, options?: IQueryOptions<T>): IQueryResult<T>

Arguments

queryFn: () => Promise<T> - The function to run. :warning: Must be memoized to avoid potential infinite loops.

options?: IQueryOptions<T> - An object containing:

  • autoRun?: boolean - indicates weither of not the query should be run automatically on component mount
  • defaultValue?: T - the default value returned by the hook

Returns

An object containing:

  • execute: () => Promise<void> - the function to execute the action
  • loading: boolean - indicates weither the action is currently pending or not
  • error?: unknown - the error potentially generated by the action
  • result: T - the value returned by the action

useFetch

:warning: Deprecated, use useQuery instead.

useFetch provides an easy way to make any action that triggers a loading, returns some data and may generate an error (typical API call use case).

It also provides a replay function to replay the action and a replace function to replace the data.

Example

import { useFetch } from '@saramorillon/hooks'
import { getData } from './service'

function MyComponent() {
  const [data, status, refresh] = useFetch(getData, null)

  if (status.loading) return <Spinner />
  if (status.error) return <Error />
  return (
    <>
      <Button onClick={refresh} />
      <Table data={data} />
    </>
  )
}

API

useFetch<T>(fetchFn: () => Promise<T>, defaultValue: T, autoFetch: boolean): [T, IFetchedStatus, () => void, React.Dispatch<React.SetStateAction<T>>]

Arguments

fetchFn: () => Promise<T> - The action to run. :warning: Must be memoized to avoid potential infinite loops.

defaultValue: T - The default value to return.

autoFetch: boolean - Weither the fetch function should be run on mount or not. Default is true.

Returns

An array containing:

  • T - the value returned by the action
  • IFetchedStatus - an object containing
    • loading: boolean - indicates weither the action is currently pending or not
    • error: unknown - the error potentially generated by the action
  • () => void - a replay function
  • React.Dispatch<React.SetStateAction<T>> - a replace function

usePagination

usePagination provides an easy way to generate a full pagination system.

Example

import { usePagination } from '@saramorillon/hooks'
import { getData } from './service'

const limit = 10

function MyComponent() {
  const { page, maxPages, setMaxPages, first, previous, next, last, canPrevious, canNext } = usePagination()
  const fetch = useCallback(() => getData(page, limit), [page])
  const [{ data, total }] = useFetch(fetch, { data: [], total: 0 })

  useEffect(() => {
    setMaxPages(Math.ceil(total / limit))
  }, [setMaxPages, total])
}

return (
  <div>
    <button disabled={!canPrevious} onClick={first}>
      First page
    </button>
    <button disabled={!canPrevious} onClick={previous}>
      Previous page
    </button>
    <span>
      Page {page} of {maxPages}
    </span>
    <button disabled={!canNext} onClick={next}>
      Next page
    </button>
    <button disabled={!canNext} onClick={last}>
      Last page
    </button>
  </div>
)

API

usePagination(maxPage: number, initialValue = 1): IPagination

Arguments

maxPage: number - The maximum page initialValue?: number - The initial page (default 1).

Returns

An object containing:

  • page: number - the current page
  • goTo: React.Dispatch<React.SetStateAction<number>> - a function to go to a specific page
  • first: () => void - a function to go to the first page
  • previous: () => void - a function to go to the previous page
  • next: () => void - a function to go to the next page
  • last: () => void - a function to go to the last page
  • canPrevious: boolean - indicates weither the navigation to the previous page is possible
  • canNext: boolean - indicates weither the navigation to the next page is possible

useForm

useForm provides helpers to handle forms.

Example

import { useForm } from '@saramorillon/hooks'

type Data {
  name: string
}

function MyComponent({ data }: { data: Data }) {
  const save = useCallback((values: Data) => {
    console.log(values)
  }, [])
  const { values, submit, onChange, reset } = useForm(save, data)

  return (
    <form onSubmit={submit} onReset={reset}>
      <input value={values.name} onChange={(e) => onChange('name', e.target.value)} />
      <button type='submit'>Submit</button>
      <button type='reset'>Reset</button>
    </form>
  )
}

API

useForm<T>(props: IFormProps<T>): IForm<T>

Arguments

save: (values: T) => void - The save action. :warning: Must be memoized to avoid potential infinite loops.

initialValues: T - The initial values.

Returns

An object containing:

  • values: T - the form values
  • onChange: <K extends keyof T>(name: K, value: T[K]) => void - a function to change the values of the form
  • submit: (e: FormEvent) => void - a function for submitting a form
  • reset: () => void - a function to reset the form to its initial values
  • loading: boolean - indicates when the form is being submitted
  • error?: unknown - contains a potential error thrown by the save function

useCopy

useCopy provides helpers to handle clipboard copy.

Example

import { useCopy } from '@saramorillon/hooks'

function MyComponent() {
  const [authorized, status, copy] = useCopy()

  if (status.loading) return <Spinner />
  if (status.error) return <Error />

  return (
    <button onClick={() => copy('Something')} disabled={!authorized}>
      Copy
    </button>
  )
}

API

useCopy(): [boolean, ICopyStatus, () => void]

Arguments

None

Returns

An array containing:

  • authorized: boolean - indicates weither the user authorized clipboard actions on their brower
  • ICopyStatus - an object containing
    • loading: boolean - indicates weither the copy is currently pending or not
    • error: unknown - the error potentially generated by the copy
  • (data: string) => void - the copy function

useDrag/useDrop

useDrag provides helpers to help dragging elements.

Example

import { useDrag, useDrop } from '@saramorillon/hooks'

function MyList() {
  const [isDragged, events] = useDrag()

  const onDrop = useCallback((source: string, target: string) => {
    console.log(`Dragged ${source} on ${target}`)
  }, [])

  return (
    <ul>
      <MyListSlot item="Item1" onDrop={onDrop} />
      <MyListSlot item="Item2" onDrop={onDrop} />
      <MyListSlot item="Item3" onDrop={onDrop} />
    </ul>
  )
}

function MyListSlot({ item, onDrop }: { item: string; onDrop: (source: string, target: string) => void }) {
  const onMove = useCallback((source: string) => onDrop(source, item), [item])
  const [isOver, events] = useDrop(onMove)

  return (
    <div {...events} style={{ backgroundColor: isOver ? 'yellow' : 'transparent' }}>
      <MyListItem item={item} />
    </div>
  )
}

function MyListItem({ item }: { item: string }) {
  const [isDragged, events] = useDrag(item)

  return (
    <div draggable {...events} style={{ color: isDragged ? 'red' : 'blue' }}>
      {item}
    </div>
  )
}

API

useDrag(source: string): [boolean, IDragEvents]
useDrop(onDrop: (source: string) => void): [boolean, IDropEvents]

Arguments

source: string - The source element. Don't forget to memoize stringified JSON objects.

onDrop: (source: string) => void - The drop action.

Returns

An array containing:

  • isDragged: boolean - indicates weither the item is currently dragged
  • IDragEvents - an object containing events to attach to the draggable item

An array containing:

  • isOver: boolean - indicates weither a dragged item is currently over the target
  • IDropEvents - an object containing events to attach to the target

useDialog

useDialog provides helpers to show and hide native HTML dialogs.

Example

import { useDialog } from '@saramorillon/hooks'

function MyComponent() {
  const { ref, visible, show, hide } = useDialog()

  return (
    <>
      <button onClick={show}>Show dialog</button>
      <dialog ref={ref} onClick={hide}>
        {visible && (
          <div onClick={(e) => e.stopPropagation()}>
            <button onClick={hide}>Hide dialog</button>
          </div>
        )}
      </dialog>
    </>
  )
}

API

useDialog(): IDialog

Arguments

None

Returns

An object containing:

  • ref: RefObject<HTMLDialogElement> - the dialog ref
  • visible: boolean - indicates weither the dialog is visible or not
  • show: () => void - a function to show the dialog
  • hide: () => void - a function to hide the dialog

Contribute

Any PR is welcomed! Please be sure to meet following requirements when posting a PR:

  • Unit tests pass and code coverage is over 90%
  • Code conventions have been respected (yarn lint can help)
  • Code is properly formatted (yarn format can help)