1.2.6 • Published 2 years ago

use-http-request-hook v1.2.6

Weekly downloads
-
License
ISC
Repository
-
Last release
2 years ago

use-http-request-hook

Utility to aid in reducing Redux boilerplate for API queries by creating an easy to use interface for requesting data. Functions similarly to rtk-query albeit with various differences. Handles the following:

  • Data fetch caching and cache update
  • Data fetch predicates
  • Handling concurrent requests
  • Data synchronization between tabs
  • Request cancellation with signal
  • Lifecycle methods
    • Overriding data
    • Resetting data

Prerequisites

Usage of the library requires the following dependencies: 1. React 16+ 2. Redux 7+

Installation

To install the library, run the following command:

npm install --save use-http-request-hook

Setup

In order to get started, we need to register the reducer:

import { combineReducers } from 'redux';

import useRequestReducer, {
  reducerName as useRequestReducerName,
} from 'use-http-request-hook/lib/reducer';

export default combineReducers({
  [useRequestReducerName]: useRequestReducer,
});

Global Configuration

Because various HTTP clients (i.e. axios) may return data differently, configuration of a global response resolver can be done:

import { configure } from 'use-http-request-hook'

configure.setResponseResolver((response) => {
  return response.data;
});

In the above, we expect that when useRequest is invoked, the request parameter function will return data nested under the data fields. For example, with function such () => axios.get('https://google.com'); the data returned would appear as such:

{
  "data": ...
}

In this case, we configure the global response resolver to resolve and return data under the .data path.

Basic Usage

Once configured, the entrypoint to using the library is through the useRequest function which can be imported as follows:

import useRequest from 'use-http-request-hook';

const useUsers = () =>
  useRequest(`users`, { // 'users' is the redux dataPath
    request: () => axios.get('https://example.com/api/v1/users') // the request function is called and will query the api url
  });

const { isLoading, data } = useUsers();

In the above, data will be stored under the users redux path, and will query https://example.com/users when ran. See below for more information about input requirements and returned values.

Input Parameters

The useRequest function takes in 2 parameters: a dataPath and a configuration. The data path is required as a unique key for storage in the global Redux store. The minimum configuration requires a request field, containing a function that returns any value (as seen in the above Basic Usage example).

Configuration Fields

All fields that can be passed via the configuration to useRequest.

NameTypeDefaultDescription
request(signal) => any or (signal) => PromiseundefinedCallback function will be triggered, when cache time expires, or dataPath changes (dataPath should be string)
isFetchbooleantrueWhether to call config.request method
cacheTimenumber2*60*1000Cache time in milliseconds
cacheTimeOnErrornumber200In order to prevent subsequent requests on fail we have cacheTimeOnError in milliseconds, when fetch was not successful
onError(responseError) => voidundefinedCallback function for handling error when config.request method fails
isSyncActionbooleanfalseWill synchronize fetched data between different tabs (Requires Integration with redux-state-sync. see docs. below)
isPersistbooleanfalseWill try to take data from localStorage and set it initially. Data still will be fetched for invalidation
appProgressbooleanfalseWill add appProgress for redux action meta

Note: Bolded fields are required.

Output Parameters

Calling the useRequest function will return an object containing the following fields:

NameTypeDefaultDescription
isLoadingbooleanundefinedData loading status
dataanyundefinedReturned data from config.request callback
erroranyundefinedReturned error from config.request callback
getDataCan be used to re-fetch data manually (It will call config.request callback)
setDataWe can set data manually (setData({ path, data }). By default path will be equal to dataPath(Unique path key to data stored in global store))
resetDataWe can reset data to default (resetData({ path }) or resetData(). By default path will be equal to dataPath(Unique path key to data stored in global store). We can also pass a function resetData((store) => path[]). Function will accept store and should return array of paths)
lastFetchedAttimestampundefinedLast time the data was refreshed

Examples

Handling concurrent requests

In this example there will be only one request made in case equal userId. It checks by dataPath parameter, and will ignore all subsequent requests with the same dataPath.

const useUser = ({ userId }) =>
  useRequest(`users:${userId}`, {
  request: () => axios.get(`https://example.com/api/v1/users/${userId}`),
  isFetch: userId
});

const ComponentA = ({ userId }) => {
  const { userId } = props;
  const { isLoading, data } = useUser({ userId });
  
  return (
    <Spinner isLoading={isLoading}>
          // ...
    </Spinner>
  );
};

const ComponentB = ({ userId }) => {
  const { userId } = props;
  const { isLoading, data } = useUser({ userId });

  return (
    <Spinner isLoading={isLoading}>
    // ...
    </Spinner>
  );
};

const MainComponent = (props) => (
  <>
    <ComponentA userId={props.userId} />
    <ComponentB userId={props.userId} />
  </>
)

Override data caching

By default, data is cached for 2 minutes. To override this value, the cacheTime variable can be specified.

const useUsers = () =>
  useRequest(`users`, { // 'users' is the redux dataPath
    request: () => axios.get('https://example.com/api/v1/users'), // the request function is called and will query the api url
    cacheTime: 1000 // 1000ms
  });

Disabling the cache can be done by specifying a value of 0 for cacheTime.

Predicated fetching

In certain cases, we may not want to fetch data until a prerequisite value is fetched. Consider the case where we want to fetch notes for a user based on his ID, but we only have his username to start with. In a case like this we need to load the users ID first before making the call.

const getNotes = ({ userId }) =>
  useRequest(`users/${userId}/notes`, { 
    request: () => axios.get(`https://example.com/api/v1/users/${userId}/notes`),
    isFetch: userId // trigger the fetch only when userId is available
  });

By configuring an isFetch predicate, calls to the getNotes function won't trigger unless the userId value exists.

Showing a loading indicator

In some contexts of a React application, you may want to show a loading indicator until data is retrieved from a server. This is made easy with the isLoading value returned by the function.

const useUser = ({ userId }) =>
  useRequest(`users:${userId}`, { 
    request: () => axios.get(`https://example.com/api/v1/users/${userId}`), 
    isFetch: userId
  });

const User = (props) => {
    const { userId } = props;
    const { isLoading, data } = useUser({ userId });
    
    return (
        <Spinner isLoading={isLoading}>
          // ...  
        </Spinner>
    );
}

Integrating with redux-state-sync

To set up an integration with the redux-state-sync library, we need configure and register the provided redux middleware.

import { applyMiddleware, createStore, compose } from 'redux';
import { createStateSyncMiddleware, initStateWithPrevTab } from 'redux-state-sync';

const syncConfig = {
  broadcastChannelOption: { type: 'localstorage' },
  predicate: (action) => action.meta && action.meta.isSyncAction,
};

const middlewares = [
  // Any custom redux middlware
  createStateSyncMiddleware(syncConfig),
];

const store = createStore(reducers, compose(applyMiddleware(...middlewares)));

initStateWithPrevTab(store);

export default store;

In the above, we define a predicate where actions must have the meta of isSyncAction to synchronize. With this in mind, we need to specify this parameter when performing calls with useRequest. Usage would look like:

const useUser = ({ userId }) =>
        useRequest(`users:${userId}`, {
          request: () => axios.get(`https://example.com/api/v1/users/${userId}`),
          isFetch: userId,
          isSyncAction: true
        });

Request cancellation with AbortController

Starting from version 1.2.0 request callback function gets as argument signal from AbortController. This signal will be invoked once hook will be unmounted. It will give developers ability to cancel all api requests. AbortController works with fetch and axios starting from version 0.22.0.

With Axios

const useUser = ({ userId }) =>
        useRequest(`users:${userId}`, {
          request: (signal) => axios.get(`https://example.com/api/v1/users/${userId}`, { signal }),
        });

With Fetch

const useUser = ({ userId }) =>
        useRequest(`users:${userId}`, {
          request: (signal) => fetch(`https://example.com/api/v1/users/${userId}`, { signal }),
        });
1.2.6

2 years ago

1.2.5

2 years ago

1.2.4

2 years ago

1.2.3

2 years ago

1.2.2

2 years ago

1.2.1

2 years ago

1.2.0

2 years ago

1.1.1

2 years ago

1.1.0

2 years ago

1.0.4

2 years ago

1.0.3

2 years ago

1.0.2

2 years ago

1.0.1

2 years ago

1.0.0

2 years ago