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

@react-hook/cache

v1.1.1

Published

A React hook for accessing an async cache that persists data between renders and components. This allows you to do neat stuff like preload data before your next page or component has even started mounting.

Downloads

2,418

Readme

A React hook for accessing an asynchronous key/value cache that persists data between renders and components. This allows you to do neat stuff like preload data before your next page or component has even started mounting.

Contents

| Section | Description | | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | Quick Start | A usage example | | createCache() | Creates an asynchronous LRU cache which can be used with the useCache() hook. Cache keys must be a string type. | | useCache() | A hook for reading and loading items from a persistent cache created by the createCache() function. |

Quick Start

Check out this example on CodeSandbox

import * as React from 'react'
import {createCache, useCache} from '@react-hook/cache'

// Creates a fetch cache w/ a max of 400 entries for JSON requests
const fetchCache = createCache(async (key, options) => {
  const response = await fetch(key, options)
  return response.json()
}, 400)

const Todo = ({id: initialId = 1}) => {
  const [id, setId] = React.useState(initialId)
  const [{status, value, error, cancel}, fetchTodo] = useCache(
    fetchCache,
    `https://jsonplaceholder.typicode.com/todos/${id}`
  )
  // Loads the todo
  React.useEffect(() => {
    // We only want to load our todo if we don't already have it in this
    // case. BUT because the value always persists between 'success' states
    // you could always be revalidating here regardless and change the if ... else
    // blocks to check 'value' instead of 'status' to determine if the todo
    // should display
    if (status === 'idle') {
      fetchTodo()
    }
  }, [fetchTodo, id, status])

  if (status === 'loading') {
    return (
      <div>
        Loading {id}...
        <button onClick={cancel}>Cancel</button>
      </div>
    )
  } else if (status === 'error') {
    return (
      <div>
        Error: {error.mesage} <button onClick={fetchTodo}>Try again</button>
      </div>
    )
  } else if (status === 'cancelled') {
    return (
      <div>
        Cancelled <button onClick={fetchTodo}>Load again</button>
      </div>
    )
  } else if (status === 'success') {
    const nextId = id + 1

    return (
      <div>
        <h1>{value.title}</h1>
        <button
          // Preloads the next todo on mouse enter
          onMouseEnter={() =>
            fetchCache.load(
              `https://jsonplaceholder.typicode.com/todos/${nextId}`
            )
          }
          // Proceeds to the next todo
          onClick={() => setId(nextId)}
        >
          Next todo
        </button>
      </div>
    )
  }
  // 'idle' status
  return null
}

API

createCache(resolver, lruSize)

Creates an asynchronous LRU cache which can be used with the useCache() hook. Cache keys must be a string type.

export const createCache = <Value = any, ErrorType = Error>(
  resolve: (key: string, ...args: any[]) => Promise<Value>,
  lruSize = Infinity
): Cache<Value, ErrorType>

Arguments

| Argument | Type | Default | Required? | Description | | -------- | ------------------------------------------------- | ---------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | resolve | (key: string, ...args: any[]) => Promise<Value> | | Yes | A callback that returns the value for a key. ...args in the callback signature are provided by the user when they call cache.load(). | | lruSize | number | Infinity | No | The max number of key/value pairs to cache at a given time. When the number of cache items exceeds this number, the item least recently accessed will be purged from the cache. |

Returns Cache

useCache(cache, key, ...args)

A hook for reading and loading items from a persistent cache created by the createCache() function.

export const useCache = <
  Value = any,
  ErrorType = Error,
  Args extends any[] = any[]
>(
  cache: Cache<Value, ErrorType, Args>,
  key: string,
  ...args: Args
): [
  UseCacheState<Value, ErrorType>,
  () => Promise<CacheState<Value, ErrorType>>
]

Arguments

| Argument | Type | Required? | Description | | -------- | ----------------- | --------- | ----------------------------------------------------------- | | cache | Cache | Yes | A cache created by the createCache() function | | key | string | Yes | The cache key to read or load from the cache | | ...args | [] | No | Arguments passed to the cache.load(key, ...args) function |

Returns [UseCacheState, () => Promise<CacheState<Value, ErrorType>>]

The second element in the array will load the value associated with the provided key and reload it in the case that a value already exists.

Types

Cache

export type Cache<
  Value = any,
  ErrorType = Error,
  Args extends any[] = any[]
> = {
  /**
   * Loads a `key` and provides its other ...args to the resolver
   */
  load: (key: string, ...args: Args) => Promise<CacheState<Value, ErrorType>>
  /**
   * Reads a `key` in the LRU cache and returns its value if there is one, otherwise
   * returns undefined
   */
  read: (key: string) => CacheState<Value, ErrorType> | undefined
  /**
   * Cancels any pending promises for `key`
   */
  cancel: (key: string) => void
  /**
   * Returns a {[key: string]: CacheState} object. This can be used
   * for persisting the state rendered on a server to the client.
   */
  readAll: () => CacheExport<CacheState<Value, ErrorType>>
  /**
   * Writes a {[key: string]: CacheState} to the LRU cache. This can be used
   * for persisting the state rendered on a server to the client.
   */
  write: (input: CacheExport<Value, ErrorType>) => void
  /**
   * Subscribes to changes to `key`. That is, `callback` will be invoked
   * any time the state assigned to `key` changed.
   */
  subscribe: (
    key: string,
    callback: CacheSubscribeCallback<CacheState<Value, ErrorType>>
  ) => void
  /**
   * Unsubscribes to changes to `key`
   */
  unsubscribe: (
    key: string,
    callback: CacheSubscribeCallback<CacheState<Value, ErrorType>>
  ) => void
}

CacheState

export type CacheState<Value = any, ErrorType = Error> =
  | {
      id: number
      /**
       * The cache is currently loading a value for this key
       */
      status: 'loading'
      /**
       * This will be the previous value if there is one, otherwise undefined
       */
      value: Value | undefined
      /**
       * Loading states will never have an error message
       */
      error: undefined
    }
  | {
      id: number
      /**
       * The cache has successfully loaded a value for the key
       */
      status: 'success'
      /**
       * This is the value loaded by the cache
       */
      value: Value
      /**
       * Success states will never have an error message
       */
      error: undefined
    }
  | {
      id: number
      /**
       * The cache encountered an error when loading a value for the key
       */
      status: 'error'
      /**
       * This is the previous value if there is one, otherwise undefined
       */
      value: Value | undefined
      /**
       * This is the error object that was caught during execution
       */
      error: ErrorType
    }
  | {
      id: number
      /**
       * The request for this key was cancelled before it was completed
       */
      status: 'cancelled'
      /**
       * This is the previous value if there isone, otherwise undefined
       */
      value: Value | undefined
      /**
       * Cancelled states never have an error message
       */
      error: undefined
    }

UseCacheState

export type UseCacheState<Value = any, ErrorType = Error> =
  | {
      /**
       * The key does not exist in the cache and the cache has not started
       * loading this key
       */
      status: 'idle'
      /**
       * When idle we have no current or previous value available
       */
      value: undefined
      /**
       * No errors will be reported here
       */
      error: undefined
      /**
       * Cancelling will have no effect when idle
       */
      cancel: () => void
    }
  | {
      /**
       * The next value for the key is currently loading in the cache
       */
      status: 'loading'
      /**
       * The previous value for this key will persist during the loading phase.
       */
      value: Value | undefined
      /**
       * No errors will be reported
       */
      error: undefined
      /**
       * Cancelling will prevent the value returned in this request from being
       * added to state
       */
      cancel: () => void
    }
  | {
      /**
       * The key does not exist in the cache and the cache has not started
       * loading this key
       */
      status: 'cancelled'
      /**
       * The previous value for this key will persist during the loading phase.
       */
      value: Value | undefined
      /**
       * No errors will be reported
       */
      error: undefined
      /**
       * Cancelling has no effect here
       */
      cancel: () => void
    }
  | {
      /**
       * We have successfully received a value for the key
       */
      status: 'success'
      /**
       * The value returned by the successful request
       */
      value: Value
      /**
       * No errors will be reported here
       */
      error: undefined
      /**
       * Cancelling will have no effect here
       */
      cancel: () => void
    }
  | {
      /**
       * The promise in the cache encountered an error
       */
      status: 'error'
      /**
       * The last successful value received by the cache will persist here
       */
      value: Value | undefined
      /**
       * This is the error object encountered by the request
       */
      error: ErrorType
      /**
       * Cancelling will have no effect here
       */
      cancel: () => void
    }

LICENSE

MIT