1.9.0 • Published 5 years ago

react-use-rest v1.9.0

Weekly downloads
3
License
MIT
Repository
github
Last release
5 years ago

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

NameTypeDefaultDescription
axiosaxios instanceaxiosYou can pass in a custom axios instance to use (for advanced usage with injected headers, etc)
autoloadbooleantruedata will fire initial GET by default unless set to false
fetchOptionsobjectundefinedOptions to be passed into fetch requests if using internal native fetch (e.g. { fetchOptions: { headers: { Authorization: 'foo' } } })
filterfunctionundefinedfilters data results into the "filtered" collection
getIdfunction(item) => item.idhow to derive item ID from a collection item (used for endpoint generation for PUT/PATCH/DELETE
initialValueobject or array[] or undefinedinitial value of "data" return, [] if collection, undefined if ID endpoint
intervalnumberundefinedrefresh collection every 5000ms (5s)
isCollectionboolean |false` | set to false to allow direct REST against a specific endpoint
logboolean or functionfalseif passed true, uses console.log for debug output, otherwise accepts any function
mergeOnCreatebooleantrueuse response payload for newly created items
mergeOnUpdateboolean |true` | use response payload for newly updated items
mockbooleantruesimulate, but do not fire POST/PUT/PATCH/DELETE actions (for testing)
onAuthenticationErrorfunctionundefinedfired when calls return 401 or 403 (e.g. can redirect, etc)
onCreatefunctionundefinedfired when item is created successfully
onErrorfunctionconsole.errorfired on internal error, or response errors
onLoadfunctionundefinedfired when data is loaded successfully
onRemovefunctionundefinedfired when item is deleted successfully
onReplacefunctionundefinedfired when item is replaced successfully
onUpdatefunctionundefinedfired when item is updated successfully
persistbooleanfalsewill persist results to localStorage for fast delivery on page refresh
queryobject or functionundefinedcan 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 })
transformfunctionundefineduse 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)
1.9.0

5 years ago

1.8.3

5 years ago

1.8.2

5 years ago

1.8.1

5 years ago

1.8.0

5 years ago

1.7.4

5 years ago

1.7.3

5 years ago

1.7.2

5 years ago

1.7.1-next.0

5 years ago

1.7.1

5 years ago

1.7.0

5 years ago

1.6.1

5 years ago

1.6.0

5 years ago

1.5.4

5 years ago

1.5.3

5 years ago

1.5.2

5 years ago

1.5.1

5 years ago

1.5.0

5 years ago

1.4.3-next.4

5 years ago

1.4.3-next.3

5 years ago

1.4.3-next.2

5 years ago

1.4.3-next.1

5 years ago

1.4.3-next.0

5 years ago

1.4.3

5 years ago

1.4.2

5 years ago

1.4.1

5 years ago

1.4.0

5 years ago

1.3.0

5 years ago

1.2.0

5 years ago

1.1.1

5 years ago

1.1.0

5 years ago

1.0.2

5 years ago

1.0.1

5 years ago

1.0.0

5 years ago

0.6.11-next.3

5 years ago

0.6.11-next.2

5 years ago

0.6.11-next.1

5 years ago

0.6.11-next.0

5 years ago

0.6.11

5 years ago

0.6.10

5 years ago

0.6.9

5 years ago

0.6.8

5 years ago

0.6.7

5 years ago

0.6.6

5 years ago

0.6.5

5 years ago

0.6.4

5 years ago

0.6.3

5 years ago

0.6.2

5 years ago

0.6.1

5 years ago

0.6.0

5 years ago

0.5.5

5 years ago

0.5.4

5 years ago

0.5.3

5 years ago

0.5.2

5 years ago

0.5.1

5 years ago

0.5.0

5 years ago

0.4.3

5 years ago

0.4.2

5 years ago

0.4.1

5 years ago

0.4.0

5 years ago

0.3.0

5 years ago

0.2.3

5 years ago

0.2.2

5 years ago

0.2.1

5 years ago

0.2.0

5 years ago

0.1.1

5 years ago

0.1.0

5 years ago