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-use-rest

v1.9.0

Published

React.js data hooks for REST endpoints

Downloads

29

Readme

react-use-rest

React.js data hooks for REST API endpoints

minified + gzipped size npm version Build Status via Travis CI

Purpose

Makes data fetching and CRUD operations against any REST endpoint this easy.

import React from 'react'
import { createRestHook } from 'react-use-rest'

// create a data hook... this would likely be done elsewhere and imported here
const useKittens = createRestHook('/api/kittens')

export default function MyApp() {
  let { data: kittens, isLoading } = useKittens() // use it, and store the results

  return <div>{isLoading ? 'loading kittens...' : `we found ${kittens.length} kittens!`}</div>
}

continue to other examples...

Features

  • [x] auto-loading
  • [x] complete REST (GET/POST/PUT/PATCH/DELETE)
  • [x] collections, items in collections, or fixed endpoints
  • [x] polling
  • [x] transforming payloads
  • [x] filtering payload results
  • [x] queries (static via object, or dynamic via function)
  • [x] collections self-maintain after POST/PUT/PATCH/DELETE
  • [x] event handling for errors, after responses, and on authentication failures
  • [x] specify how to derive id from collection items (used to generate endpoints like /api/items/3)
  • [x] persist non-sensitive results to prevent load time waits (while still updating after fetch)
  • [x] data is shared across components without context or prop-drilling, thanks to use-store-hook
  • [x] GET requests shared using internal pooling to cut down on duplicate network requests

Examples


API

Options

| Name | Type | Default | Description | | ------------------------- | :-------------------------------------------------------------------------------: | :-----------------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | axios | axios instance | axios | You can pass in a custom axios instance to use (for advanced usage with injected headers, etc) | | autoload | boolean | true | data will fire initial GET by default unless set to false | | fetchOptions | object | undefined | Options to be passed into fetch requests if using internal native fetch (e.g. { fetchOptions: { headers: { Authorization: 'foo' } } }) | | filter | function | undefined | filters data results into the "filtered" collection | | getId | function | (item) => item.id | how to derive item ID from a collection item (used for endpoint generation for PUT/PATCH/DELETE | | initialValue | object or array | [] or undefined | initial value of "data" return, [] if collection, undefined if ID endpoint | | interval | number | undefined | refresh collection every 5000ms (5s) | | isCollection | boolean |false| set to false to allow direct REST against a specific endpoint | | **log** | booleanorfunction | false | if passedtrue, uses console.logfor debug output, otherwise accepts any function | | **mergeOnCreate** | boolean | true | use response payload for newly created items | | **mergeOnUpdate** | boolean |true | use response payload for newly updated items | | mock | boolean | true | simulate, but do not fire POST/PUT/PATCH/DELETE actions (for testing) | | onAuthenticationError | function | undefined | fired when calls return 401 or 403 (e.g. can redirect, etc) | | onCreate | function | undefined | fired when item is created successfully | | onError | function | console.error | fired on internal error, or response errors | | onLoad | function | undefined | fired when data is loaded successfully | | onRemove | function | undefined | fired when item is deleted successfully | | onReplace | function | undefined | fired when item is replaced successfully | | onUpdate | function | undefined | fired when item is updated successfully | | persist | boolean | false | will persist results to localStorage for fast delivery on page refresh | | query | object or function | undefined | can send fixed query params via object such as { isLive: true } or via a dynamically executed query function (executed at time of load/interval), such as () => ({ isLive: Math.random() > 0.5 }) | | transform | function | undefined | use to reshape your API payload (e.g. (data) => data.data.slice(0,2) |

Example 2

(all options/returns exposed)

import React from 'react'
import { createRestHook } from 'react-use-rest'

// create a data hook
const useKittens = createRestHook('/api/kittens') // any options may be included here for convenience

export default function MyApp() {
  // instantiate data hook with options (all options may also be passed at time of creation [above])
  let {
    data = [],                      // data returned from API (defaults to empty array)
    filtered = [],                  // data, as filtered with filter function (options) responds to changes in filter or data
    isLoading,                      // isLoading flag (true during pending requests)
    error,                          // API error (if any) - this is
    key,                            // random render-busting attr to explode into a component on data changes.  Looks like { key: 123556456421 }
    update,                         // PATCH fn(item, oldItem) - sends only changes via PATCH (if changed)
    replace,                        // PUT fn(item, oldItem) - sends full item via PUT (if changed)
    remove,                         // DELETE fn(item, oldItem) - deleted item
    create,                         // POST fn(item, oldItem) - creates item
    load,                           // refresh/load data via GET
    refresh                         // alias for load()
  } = useKittens({
    axios: myAxiosInstance,         // can pass in a custom axios instance to use (for advanced usage)
    autoload: true,                 // data will fire initial GET by default unless set to false,
    filter: item => item.age > 5,   // filters data results into the "filtered" collection,
    getId: item => item._id,        // tell the hook how to derive item ID from a collection item
    initialValue: []                // initial value of "data" return (defaults to [] if collection assumed)
    interval: 5000,                 // refresh collection every 5000ms (5s),
    isCollection: false             // set to false to allow direct REST against a specific endpoint
    log: true                       // enable console.log output
    mergeOnCreate: true             // use response payload for newly created items (default: true)
    mergeOnUpdate: true             // use response payload for newly updated items (default: true)
    onAuthenticationError: (err) => {},  // fired when calls return 401 or 403 (can redirect, etc)
    onCreate: (err) => {},          // fired when item is created successfully
    onError: (err) => {},           // fired on internal error, or response errors
    onLoad: (data) => {},           // fired when data is loaded successfully
    onRemove: (item) => {},         // fired when item is deleted successfully
    onReplace: (item) => {},        // fired when item is replaced successfully
    onUpdate: (item) => {},         // fired when item is updated successfully
    log: true                       // enable console.log output
    mock: true,                     // only simulate POST/PUT/PATCH/DELETE actions (for testing)
    onError: console.warn           // do something custom with error events (e.g. toasts, logs, etc)
    persist: true,                  // will persist results to localStorage for fast delivery on page refresh
    query: { isCute: true },        // can send fixed query params via object or....
    query: () => ({ isCute: Math.random() > 0.1 }) // via function executed at time of [every] load
    transform: data =>
      data.kittens.slice(0,5),      // in case you need to reshape your API payload
  })

  return (
    <ul>
      {
        data.map(kitten => (
          <li key={kitten._id}>
            { kitten.name } -
            <button onClick={() => remove(kitten)}>
              Delete
            </button>
          </li>
        ))
      }
    </ul>
  )
}

Example 3

(chained hooks, e.g. collection and item details)

import React from 'react'
import { createRestHook } from 'react-use-rest'

// create a data hook
const useKittens = createRestHook('/api/kittens') // any options may be included here for convenience

export default MyApp = () => {
  // quick tip: { persist: true } loads cached content at page load, then fires the GET and updates
  // as necessary to prevent stale data
  let { data: kittens } = useKittens({ persist: true })
  let [selectedKitten, setSelectedKitten] = useState()

  let { data: kittenDetails } = useKittens(selectedKitten)

  if (isLoading && !collections.length) {
    return <p>Loading...</p>
  }

  return (
    <div>
      {kittens.map(kitten => (
        <button key={kitten.id} onClick={() => setSelectedKitten(kitten.id)}>
          {kitten.name}
        </button>
      ))}

      <h1>Payload</h1>
      {JSON.stringify(kittenDetails, 2, null) // will reload whenever selectedKitten changes
      }
    </div>
  )
}

Example 4

(generate and load hook dynamically from props)

import React, { useState, useEffect } from 'react'
import { createRestHook } from 'react-use-rest'

// create a curried function to dynamically return a data hook from a collection name
const useCollectionItems = (collectionName = '') => createRestHook(`/api/${collectionName}`)

export const ViewCollectionItem = ({ collectionName, itemId }) => {
  console.log('viewing collection item', itemId, 'in', collectionName)

  // { collectionName: 'kittens', itemId: 3 } will generate a dynamic hook
  // with endpoint '/api/kittens', and passing in the itemId, will load the hook as an item
  // with endpoint '/api/kittens/3'

  let { data: itemDetails } = useCollectionItems(collectionName)(itemId)

  return <div>{item ? JSON.stringify(itemDetails, null, 2) : null}</div>
}

Example 5

(redirect to login on 401)

import React from 'react'
import { createRestHook } from 'react-use-rest'

// create a data hook that might see a 401/Unauthorized
const useKittens = createRestHook('/api/kittens', {
  onAuthenticationError: err => (window.location.href = '/login?returnTo=' + encodeURIComponent(window.location.href)),
})

export default function MyApp() {
  let { data } = useKittens()

  // if loading /api/kittens would fire a 401, the app
  // redirects to /login with enough info to return once logged in

  return <div>{isLoading ? 'loading kittens...' : `we found ${data.length} kittens!`}</div>
}

Changelog

  • v1.9.0 - replaced internal use-store-hook with updated module location use-store to avoid deprecation notices
  • v1.8.0 - decreased module size to 4.3k gzipped
  • v1.7.3 - fix: re-embeds default Content-Type: application/json header
  • v1.7.1 - converted from babel to rollup + typescript to decrease module size
  • v1.7.0 - added fetchOptions option (allows for custom headers to be passed with hook requests)
  • v1.6.0 - removed deepmerge dependency (previously used for options merging)