Skip to content

rcharmeyer/react-utils

Repository files navigation

react-utils

My library of React utilities.

Hooks

Scopes & Hoisting State

Using createStore

const AccordionScope = createScope ()

const openIdStore = createStore (() => {
  const [ openId, setOpenId ] = useState (null)
  return { openId, setOpenId }
}, [ AccordionScope ])

function AccordionItem ({ id, children }) {
  const { openId, setOpenId } = useStore (openIdStore)
  const open = openId === id
  const toggleOpen = useEvent (() => setOpenId (open ? null : id))

  return (
    <Expandable open={open} onToggle={toggleOpen}>
      {children}
    </Expandable>
  )
}

Adding createStoreFamily

const AccordionScope = createScope ()

const openIdStore = createStore (() => {
  const [ openId, setOpenId ] = useState (null)
  return { openId, setOpenId }
}, [ AccordionScope ])

const openStoreBy = createStoreFamily ((id) => {
  const { openId, setOpenId } = useStore (openIdStore)
  const open = openId === id
  const toggleOpen = useEvent (() => setOpenId (open ? null : id))
  return { open, toggleOpen }
}, [ AccordionScope ])

function AccordionItem ({ id, children }) {
  const { open, toggleOpen } = useStore (openStoreBy (id))
  return (
    <Expandable open={open} onToggle={toggleOpen}>
      {children}
    </Expandable>
  )
}

Using hoist instead of create

const AccordionScope = createScope ()

const useOpenIdState = hoist (() => {
  const [ openId, setOpenId ] = useState (null)
  return { openId, setOpenId }
}, [ AccordionScope ])

const useOpenToggleBy = hoist ((id) => {
  const { openId, setOpenId } = useStore (openIdStore)
  const open = openId === id
  const toggleOpen = useEvent (() => setOpenId (open ? null : id))
  return { open, toggleOpen }
}, [ AccordionScope ])

function AccordionItem ({ id, children }) {
  const { open, toggleOpen } = useOpenToggleBy (id)
  return (
    <Expandable open={open} onToggle={toggleOpen}>
      {children}
    </Expandable>
  )
}

Utilities

useEvent

Based on this React RFC of the same name.

This hook is a lot like useCallback but it requires no deps because the reference is always stable and always represents the latest callback. However, unlike useCallback it does not support returning values or async functions.

function Expandable ({ children }) {
  const [ active, setActive ] = useState (false)

  const toggleActive = useEvent (() => {
    setActive (!active)
  })

  // Button never re-renders, not possible with useCallback
  return <>
    <Button onClick={toggleActive}>
    {active && <div>{children}</div>}
  </>
}

useStruct

// with useStruct
function useTuple (a, b) {
  return useStruct ([ a, b ])
}

// without
function useTuple (a, b) {
  return useMemo (() => {
    return [a, b] as const
  }, [a, b])
}

useMemoShallow

useMemo except it will also check for shallow equality of the return value.

type Item = {
  id: string,
  tags: string[],
}

function useFilteredItems (items: Item[], tag: string) {
  return useMemoShallow (() => {
    return items.filter (item => item.tags.includes (tag))
  }, [ items, tag ])
}

About

My library of React utilities.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published