2.12.4 • Published 14 days ago

@saramorillon/hooks v2.12.4

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

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)
2.12.4

14 days ago

2.11.0

9 months ago

2.12.0

9 months ago

2.10.0

10 months ago

2.12.1

8 months ago

2.9.2

1 year ago

2.9.3

1 year ago

2.9.0

1 year ago

2.8.1

1 year ago

2.8.0

1 year ago

2.9.1

1 year ago

2.7.0

2 years ago

2.5.0

2 years ago

2.6.0

2 years ago

2.4.0

2 years ago

2.3.0

2 years ago

2.3.1

2 years ago

1.2.0

2 years ago

1.3.0

2 years ago

2.2.1

2 years ago

2.2.0

2 years ago

2.1.1

2 years ago

2.2.2

2 years ago

2.1.0

2 years ago

2.0.0

2 years ago

1.1.2

2 years ago

1.1.1

2 years ago

1.1.0

2 years ago

1.0.1

2 years ago

1.0.0

2 years ago