react-use-rest

1.9.0 • Public • Published

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

  • auto-loading
  • complete REST (GET/POST/PUT/PATCH/DELETE)
  • collections, items in collections, or fixed endpoints
  • polling
  • transforming payloads
  • filtering payload results
  • queries (static via object, or dynamic via function)
  • collections self-maintain after POST/PUT/PATCH/DELETE
  • event handling for errors, after responses, and on authentication failures
  • specify how to derive id from collection items (used to generate endpoints like /api/items/3)
  • persist non-sensitive results to prevent load time waits (while still updating after fetch)
  • data is shared across components without context or prop-drilling, thanks to use-store-hook
  • 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 boolean or function false if passed true, uses console.log for 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)

Package Sidebar

Install

npm i react-use-rest

Weekly Downloads

5

Version

1.9.0

License

MIT

Unpacked Size

44.7 kB

Total Files

4

Last publish

Collaborators

  • krwhitley