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-http-fetch

v1.0.6

Published

An http library for React JS built on top of native JS fetch

Downloads

74

Readme

Contents

Just follow links below to get an overview of library features.

Getting started

Install the package by using npm

npm install react-http-fetch

or yarn

yarn add react-http-fetch

Provider

You can override the default configuration used by the http client to perform any request by using the HttpClientConfigProvider:

import React from 'react';
import { defaultHttpReqConfig, HttpClientConfigProvider } from 'react-http-fetch';

function Child() {
  return (
    <div> Child component </div>
  );
};

function httpResponseParser(res) {
  return res.json();
}


function App() {
  /**
   * Provided configs are automatically merged to the default one.
   */
  const httpReqConfig = {
    // ...defaultHttpReqConfig,
    baseUrl: process.env.BACKEND_URL,
    responseParser: httpResponseParser,
    reqOptions: {
      headers: {
        'Content-Type': 'text/html; charset=UTF-8',
      },
    },
  };

  return (
    <HttpClientConfigProvider config={httpReqConfig}>
      <Child />
    </HttpClientConfigProvider>
  );
}

export default App;

Below the complete set of options you can provide to the HttpClientConfigProvider:

| Option | Description | Default | | --------------------- | --------------------------------------------------------------------------|------------- | |baseUrl|The base url used by the client to perform any http request (e.g. http://localhost:8000)|'' |responseParser|A function that maps the native fetch response. The default parser transform the fetch response stream into a json (https://developer.mozilla.org/en-US/docs/Web/API/Response/json)|httpResponseParser |requestBodySerializer|A function used to serialize request body. The default serializer take into account a wide range of data types to figure out which type of serialization to perform|serializeRequestBody |reqOptions|The default request option that will be carried by any request dispatched by the client. See HttpRequestOptions|{ headers: { 'Content-Type': 'application/json' } } |cacheStore|The store for cached http responses. By default an in-memory cache store is used.|HttpInMemoryCacheStore |cacheStorePrefix|The prefix concatenated to any cached entry.|rfh |cacheStoreSeparator|Separates the store prefix and the cached entry identifier|__

Http client

The useHttpClient hook return a set of methods to perform http requests. The request function is the lowest level one, all other exposed functions are just decorators around it. Below a basic example using request:

import React from 'react';
import { useHttpClient } from 'react-http-fetch';

function App() {
  const { request } = useHttpClient();

  const [todo, setTodo] = useState();

  useEffect(
    () => {
      const fetchTodo = async () => {
        const res = await request({
          baseUrlOverride: 'https://jsonplaceholder.typicode.com',
          relativeUrl: 'todos/1',
          requestOptions: {
            method: 'GET',
          },
        });
        setTodo(res);
      };

      fetchTodo();
    },
    [request]
  );

  return (
    <div>{`Todo name: ${todo && todo.title}`}</div>
  );
}

export default App;

Public API

The complete public API exposed by the hook: | Method | Description | Params | Return | | --------------------- | --------------------------------------------------------------------------| --------------------- | --------------------- | |request | The lowest level method to perform a http request | Request params | Request return | getpostputpatchdelete | Make use of lower level method request by just overriding the http method (example) | Request params | Request return | abortableRequest | The lowest level method to perform an abortable http request (example) | Request params | Abortable request return | abortableGetabortablePostabortablePutabortablePatchabortableDelete | Make use of lower level method abortableRequest by just overriding the http method | Request params | Abortable request return

Request params

| Parameter | Type | Description | | --------- | ---- | ----------- | | baseUrlOverride | string | The base url of the request. If provided, it would override the provider base url. | relativeUrl | string | The url relative to the base one (e.g. posts/1). | parser | HttpResponseParser | An optional response parser that would override the provider global one. | | context | HttpContext | An optional context that carries arbitrary user defined data. See examples.| | requestOptions | HttpRequestOptions | The options carried by the fetch request. |

Request return

The jsonified return value of native JS fetch. If a custom response parser (see Provider) is provided then the return value corresponds to the parsed one.

Abortable request return

| Value | Type | | ----- | ---- | |[request, abortController]|[RequestReturn, AbortController]|

Example – Abortable request

import React, { useState, useRef } from 'react';
import { useHttpClient } from 'react-http-fetch';

function App() {
  const { abortableRequest } = useHttpClient();
  const abortCtrlRef = useRef();
  
  const [todo, setTodo] = useState();

  const fetchTodo = async () => {
    const [reqPromise, abortController] = abortableRequest({
      baseUrlOverride: 'https://jsonplaceholder.typicode.com',
      relativeUrl: 'todos/1',
    });
    abortCtrlRef.current = abortController;

    try {
      const res = await reqPromise;
      setTodo(res);
    } catch (error) {
      // Abort the request will cause the request promise to be rejected with the following error:
      // "DOMException: The user aborted a request."
      console.error(error);
    } finally {
      abortCtrlRef.current = undefined;
    }
  };

  const abortPendingRequest = () => {
    if (abortCtrlRef.current) {
      abortCtrlRef.current.abort();
    }
  };

  return (
    <div style={{ margin: '20px' }}>
      <div>{`Todo name: ${todo && todo.title}`}</div>
      <button
        style={{ marginRight: '10px' }}
        type="button"
        onClick={fetchTodo}
      >
        Do request
      </button>
      <button
        type="button"
        onClick={abortPendingRequest}
      >
        Abort
      </button>
    </div>
  );
}

export default App;

Example – Get request

import React, { useState, useEffect } from 'react';
import { useHttpClient } from 'react-http-fetch';


function App() {
  const { get } = useHttpClient();

  const [todo, setTodo] = useState();

  useEffect(
    () => {
      const fetchTodo = async () => {
        const res = await get({
          baseUrlOverride: 'https://jsonplaceholder.typicode.com',
          relativeUrl: 'todos/1',
        });
        setTodo(res);
      };

      fetchTodo();
    },
    [get]
  );

  return (
    <div>{`Todo name: ${todo && todo.title}`}</div>
  );
}

export default App;

Example – Http context

import React, { useEffect } from 'react';
import {
  useHttpClient,
  useHttpEvent,
  RequestStartedEvent,
  HttpContextToken,
  HttpContext, } from 'react-http-fetch';

const showGlobalLoader = new HttpContextToken(true);
const reqContext = new HttpContext().set(showGlobalLoader, false);

function App() {
  const { request } = useHttpClient();

  useHttpEvent(RequestStartedEvent, (payload) => {
    console.log('Show global loader:', payload.context.get(showGlobalLoader));
  });

  useEffect(
    () => {
      const fetchTodo = async () => {
        await request({
          baseUrlOverride: 'https://jsonplaceholder.typicode.com',
          relativeUrl: 'todos/1',
          context: reqContext,
        });
      };

      fetchTodo();
    },
    [request]
  );

  return (
    <h1>Http Context</h1>
  );
}

export default App;

Request hooks

The library provides a hook useHttpRequest managing the state of the http request. Such state is returned by the hook along with a function to trigger the request. See params and return for more info. A dedicated hook is provided for every http method: useHttpGet, useHttpPost, useHttpPatch, useHttpPut, useHttpDelete.

Http request hook params

| Parameter | Type | Description | | --------- | ---- | ----------- | | baseUrlOverride | string | The base url of the request. If provided, it would override the provider base url. | relativeUrl | string | The url relative to the base one (e.g. posts/1). | parser | HttpResponseParser | An optional response parser that would override the provider global one. | | context | HttpContext | An optional context that carries arbitrary user defined data. See examples.| | requestOptions | HttpRequestOptions | The options carried by the fetch request. | | initialData | any | The value that the state assumes initially before the request is send. | | fetchOnBootstrap | boolean | Tell if the fetch must be triggered automatically when mounting the component or not. In the second case we would like to have a manual fetch, this is optained by a request function returned by the hook. |

Http request hook return

Returns an array of three elements:

  • The first one embeds the state of the http request.
  • The second is a function that can be used to perform an abortable http request.
  • The third is a function that can be used to perform a non-abortable http request.

See examples for further details. The table below describes the shape (i.e. properties) of http request state.

Http request state

| Property | Type | Description | | --------- | ---- | ----------- | | pristine | boolean | Tells if the request has been dispatched. | | errored | boolean | Tells if the request has returned an error. | | isLoading | boolean | Tells if the request is pending. | | error | unknown | property evaluated by the error generated by the backend api. | | data | any | The response provided by the backend api. |

Example – Http request hook triggered automatically on component mount

import React from 'react';
import { useHttpRequest } from 'react-http-fetch';

function App() {
  const [state] = useHttpRequest({
    baseUrlOverride: 'https://jsonplaceholder.typicode.com',
    relativeUrl: 'todos/1',
    requestOptions: {},
    initialData: {},
    fetchOnBootstrap: true,
  });

  return (
    <div>{`Todo name: ${(state.data && state.data.title) || 'unknown'}`}</div>
  );
}

export default App;

Example – Http request hook triggered manually on component mount

import { useHttpRequest } from 'react-http-fetch';
import React, { useEffect } from 'react';

function App() {
  const [state, request] = useHttpRequest({
    baseUrlOverride: 'https://jsonplaceholder.typicode.com',
    relativeUrl: 'todos/1',
  });

  useEffect(() => {
    const { reqResult, abortController } = request();
    reqResult
      .then(res => console.log('request response', res))
      .catch(err => console.error(err));

    // You can use the returned AbortController instance to abort the request
    // abortController.abort();
  }, [request]);

  return (
    <div>{`Todo name: ${(state.data && state.data.title) || 'unknown'}`}</div>
  );
}

export default App;

Example – Non-abortable http request hook

Placeholder
import React, { useEffect } from 'react';
import { useHttpRequest } from 'react-http-fetch';

function App() {
  const [state, , request] = useHttpRequest({
    baseUrlOverride: 'https://jsonplaceholder.typicode.com',
    relativeUrl: 'todos/1',
  });

  useEffect(() => request(), [request]);

  return (
    <div>{`Todo name: ${(state.data && state.data.title) || 'unknown'}`}</div>
  );
}

export default App;

Example – Aborting http request triggered by the hook

import { useHttpRequest } from 'react-http-fetch';
import React, { useRef } from 'react';

function App() {
  const [state, request] = useHttpRequest({
    baseUrlOverride: 'https://jsonplaceholder.typicode.com',
    relativeUrl: 'todos/1',
  });

  const abortCtrlRef = useRef();

  const fetchTodo = () => {
    abortPendingRequest();

    const { reqResult, abortController } = request();
    abortCtrlRef.current = abortController;

    reqResult
      // Abort the request will cause the request promise to be rejected with the following error:
      // "DOMException: The user aborted a request."
      .catch(err => console.error(err));
  };

  const abortPendingRequest = () => {
    if (abortCtrlRef.current) {
      abortCtrlRef.current.abort();
    }
  };

  return (
    <div style={{ margin: '20px' }}>
      <div>{`Todo name: ${(state.data && state.data.title) || 'unknown'}`}</div>
      <button
        style={{ marginRight: '10px' }}
        type="button"
        onClick={fetchTodo}
      >
        Do request
      </button>
      <button
        type="button"
        onClick={abortPendingRequest}
      >
        Abort
      </button>
    </div>
  );
}

export default App;

Example – Http post request hook

import React, { useState } from 'react';
import { useHttpPost } from 'react-http-fetch';

function App() {
  const [inputs, setInputs] = useState({});

  const handleChange = (event) => {
    const name = event.target.name;
    const value = event.target.value;
    setInputs(values => ({...values, [name]: value}))
  }

  const [, createPostRequest] = useHttpPost({
    baseUrlOverride: 'https://jsonplaceholder.typicode.com',
    relativeUrl: 'posts',
  });

  const createPost = async (event) => {
    event.preventDefault();
    const { postTitle, postBody } = inputs;

    const reqBody = { title: postTitle, body: postBody };
    try {
      // Providing request options when running the request.
      // Provided options will be merged to the one provided
      // to the hook useHttpPost.
      await createPostRequest({
        requestOptions: { body: reqBody }
      });
      alert('Post created!');
    } catch (error) {
      console.error(error);
      alert('An error occured. Check the browser console.');
    }
  };

  return (
    <form onSubmit={createPost}>
      <label style={{ display: 'block' }}>
        Title:
        <input
          type="text"
          name="postTitle"
          value={inputs.postTitle || ""}
          onChange={handleChange}
        />
      </label>
      <label style={{ display: 'block' }}>
        Body:
        <input
          type="text"
          name="postBody"
          value={inputs.postBody || ""}
          onChange={handleChange}
        />
      </label>
      <button type="submit">
        Create Post
      </button>
    </form>
  );
}

export default App;

Events

Every time a request is executed the events shown below will be emitted. Each event carries a specific payload.

| Event type | Payload type | | --------- | ---- | | RequestStartedEvent | HttpRequest | | RequestErroredEvent | HttpError | | RequestSuccededEvent |RequestSuccededEventPayload |

You can subscribe a specific event using the useHttpEvent hook as shown below:

import { useState } from 'react';
import { RequestErroredEvent, RequestStartedEvent, RequestSuccededEvent, useHttpEvent, useHttpRequest } from 'react-http-fetch';

function App() {
  const [count, setCount] = useState(0);

  const [, request] = useHttpRequest({
    baseUrlOverride: 'https://jsonplaceholder.typicode.com',
    relativeUrl: 'todos/1',
  });

  useHttpEvent(RequestStartedEvent, () => setCount(count + 1));
  useHttpEvent(RequestSuccededEvent, () => setCount(count > 0 ? count - 1 : 0));
  useHttpEvent(RequestErroredEvent, () => setCount(count > 0 ? count - 1 : 0));

  return (
    <>
      <button onClick={request}>{'increment count:'}</button>
      <span>{count}</span>
    </>
  );
}

export default App;

Caching

Any request can be cached by setting the maxAge (expressed in milliseconds) parameter as part of the request options as shown below:

import { useHttpRequest } from 'react-http-fetch';
import React from 'react';

function App() {
  const [state, request] = useHttpRequest({
    baseUrlOverride: 'https://jsonplaceholder.typicode.com',
    relativeUrl: 'todos/1',
    requestOptions: { maxAge: 60000 } // Cache for 1 minute
  });

  const fetchTodo = () => {
    const { reqResult } = request();
    reqResult.then(res => console.log(res))
  };

  return (
    <>
      <div>
        {`Todo name: ${(state && state.data && state.data.title) || ''}`}
      </div>
      <button type="button" onClick={fetchTodo}>
        Make request
      </button>
    </>
  );
}

export default App;

By default the http client uses an in-memory cache, so it will be flushed everytime a full app refresh is performed. You can override the default caching strategy by providing your own cache store. The example below shows a http cache store based on session storage:

import React from 'react';
import { useHttpRequest, HttpClientConfigProvider } from 'react-http-fetch';

export class HttpSessionStorageCacheStore {
  /**
   * The local cache providing for a request identifier
   * the corresponding cached entry.
   */
  _store = window.sessionStorage;

  /**
   * @inheritdoc
   */
  get(identifier) {
    const stringifiedEntry = this._store.getItem(identifier);
    if (!stringifiedEntry) {
      return;
    }

    try {
      const parsedEntry = JSON.parse(stringifiedEntry);
      return parsedEntry;
    } catch (err) {
      return;
    }
  }

  /**
   * @inheritdoc
   */
  put(identifier, entry) {
    try {
      const stringifiedEntry = JSON.stringify(entry);
      this._store.setItem(identifier, stringifiedEntry);

      return () => this.delete(identifier);
    } catch (err) {
      return () => {};
    }
  }

  /**
   * @inheritdoc
   */
  has(identifier) {
    return this._store.has(identifier);
  }

  /**
   * @inheritdoc
   */
  delete(identifier) {
    this._store.removeItem(identifier);
  }

  /**
   * Gets all entry keys.
   */
  _keys() {
    return Object.keys(this._store);
  }

  /**
   * Gets all stored entries.
   */
  entries() {
    return this._keys()
      .map(entryKey => this._store.getItem(entryKey));
  }

  /**
   * @inheritdoc
   */
  flush() {
    this._keys().forEach((itemKey) => {
      this.delete(itemKey);
    });
  }
}

const httpCacheStore = new HttpSessionStorageCacheStore();

function Child() {
  const [state, request] = useHttpRequest({
    baseUrlOverride: 'https://jsonplaceholder.typicode.com',
    relativeUrl: 'todos/1',
    requestOptions: { maxAge: 60000 } // Cache for 1 minute
  });

  console.log('Request state:', state.data);

  const fetchTodo = () => {
    const { reqResult } = request();
    reqResult.then(res => console.log('Request response: ', res))
  };

  return (
    <>
      <div>
        {`Todo name: ${(state && state.data && state.data.title) || ''}`}
      </div>
      <button type="button" onClick={fetchTodo}>
        Make request
      </button>
    </>
  );
};


function App() {
  const httpReqConfig = {
    cacheStore: httpCacheStore,
    // "prefix" and "separator" are not mandatory,
    // if not provided the default ones will be used.
    cacheStorePrefix: 'customPrefix',
    cacheStoreSeparator: '-'
  };

  return (
    <HttpClientConfigProvider config={httpReqConfig}>
      <Child />
    </HttpClientConfigProvider>
  );
}

export default App;

Browser support

| Edge | Firefox | Chrome | Safari | | --------- | --------- | --------- | --------- | | last 2 versions| last 2 versions| last 2 versions| last 2 versions