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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@andrewmat/hooks

v0.4.0

Published

A collection of React custom hooks that I've developed

Downloads

24

Readme

Andrewmat/hooks

I've developed some custom hooks making the SWBD project, and now I'm sharing it. Just that.

Usage

Add it to your project

npm install @andrewmat/hooks
yarn add @andrewmat/hooks

Starting using it

import { useCache, CacheProvider } from '@andrewmat/hooks'
import { useCounter } from '@andrewmat/hooks'
import { useDocumentTitle } from '@andrewmat/hooks'
import { useIterator } from '@andrewmat/hooks'
import { useToggle } from '@andrewmat/hooks'

So... where are they?

Inside the /src/hooks there are multiple hooks that I developed. I'm avoiding using external package dependencies, and focusing only on the original React resources for now

useToggle

Standalone hook

Identity:

const [value, toggle] = useToggle([initialValue = false, toggleValue = true])

It stores one of two options, and returns the current option and a function that changes to the other option. As default, it simply changes between booleans, with false as initial value.

function MyComponent() {
  const [myBool, toggleMyBool] = useToggle()

  // button label is false/true
  return (
    <button onClick={toggleMyBool}>
      {myBool}
    </button>
  )
}

It also can receive a initial boolean value

function MyComponent() {
  const initialValue = true
  const [myBool, toggleMyBool] = useToggle(initialValue)

  // button label is true/false
  return (
    <button onClick={toggleMyBool}>
      {myBool}
    </button>
  )
}

It also can receive an array of two elements, and if so, it toggles between then. The index 0 will be the initial value in this case

function MyComponent() {
  const [theme, toggleTheme] = useToggle(['blue', 'pink'])

  return (
    <div>
      This is the {theme} theme!
      <button onClick={toggleTheme} className={`${theme}-theme`}>
        Toggle
      </button>
    </div>
  )
}

useCounter

Standalone hook

Identity:

const [value, increment] = useCounter(initialValue = 0)

It returns the current value, and an increment function, in array form

function MyComponent() {
  const [value, increment] = useCounter()
  return <button onClick={increment}>value</button>
}

useDocumentTitle

Standalone hook

Identity:

useDocumentTitle(title)

It receives an title to apply to the document. It also returns to the previous title when the component unmounts

function MyComponent() {
  useDocumentTitle('Example Title')
  return <div />
}

useIterator

Standalone hook

Identity:

const controller = useIterator(list, loop = false, startIndex = 0)
// controller: { item, index, next, previous, hasNext, hasPrevious }

It receives an array, and returns an controller to iterate in this array

function MyComponent() {
  const myList = ['Alice', 'Ben', 'Charles']
  const iterator = useIterator(list)
  return (
    <div>
      <button onClick={iterator.previous}>Previous</button>
      {iterator.item}
      <button onClick={iterator.next}>Next</button>
    </div>
  )
}

useIterator also can received two more arguments

// loop: Whether or not should the list loop. Defaults to false
// startIndex: What index should be the initial item. Defaults to 0
useIterator(list, loop, startIndex)

The controller returned by this hook is composed of the following attributes:

const iterator = useIterator(list)

// current item being iterated
iterator.item

// index of the current item
iterator.index

// function to iterate to the next item on the list
// returns the controller so it can be chained
const nextIterator = iterator.next()

// function to iterate to the previous item on the list
// returns the controller so it can be chained
const previousIterator = iterator.previous()

// boolean that detects if it has a next item on the list
// it also accounts if the iterator loops
iterator.hasNext

// boolean that detects if it has a previous item on the list
// it also accounts if the iterator loops
iterator.hasPrevious

useCache

const cacheFetch = useCache(async function () {})

A hook to use cache to store resolutions of async functions. It uses the CacheProvider (inside /src/components) that creates an context used for caching resources.

It receives an async function, and it returns an async function that mimics the given function, using caching resources whenever possible

function myFetch(id) {
  returns fetch(`${apiUrl}/${id}`)
}

function MyComponent() {
  const myCachedFetch = useCache(myFetch)

  return (
    <MyAnotherComponent onChange={myCachedFetch} />
  )
}

// useCache should be used alongside CacheProvider
function App() {
  return (
    <CacheProvider>
      <MyComponent/>
    </CacheProvider>
  )
}

It receives a namespace as second argument. If not set, it uses __root as default, althought I recommend to always use it to avoid name clashes between diferent contexts

function MyComponent() {
  const myCachedFetch = useCache(myFetch, 'myNamespace')

  return (
    <MyAnotherComponent onChange={myCachedFetch} />
  )
}

It also receives a config object as third parameter. The config object is as follows:

const cacheConfig = {
  // optional function that generates key
  // It receives an array of parameters and must return an string
  keyGenerator,

  // optional key of cache entry
  // It overwrites the keyGenerator function
  key,

  // optional key to limit entries inside the namespace
  limit
}
useCache(myFetch, 'myNamespace', cacheConfig)

The returned function also has a new attribute: clearCache, a function that removes all the entries from the cache of the given namespace

function MyComponent({ data }) {
  const cachedFetch = useCache(myFetch, { namespace: 'my-fetch' })

  return (
    <div>
      <button onClick={() => cachedFetch(data)}>Fetch data<button>
      <button onClick={cachedFetch.clearCache}>Reset cache<button>
    </div>
  )
}