My Promise is to Cache your data!

Promises have had a very boilerplate filled history with generic libraries and browser standards. With react hooks being in the mix, everything is about abstracting stuff out to make things simpler. 

What is the traditional way to call API and Cache Data?

In the traditional way, we are forced to work with too much boilerplate for calling an API and cache data. We currently have an Action -> Controller -> Decorator -> Reducer architecture but this involves too many new files that may just behave as an identity.

Also, we do not have a standard interface for caching the results of API calls. If we want to cache data in a different places then we need to develop our code accordingly.
For example,
If we want to cache data in local storage then write a code for it,
If we want to cache data in memory cache then write a code for it,
And the same goes for indexdDB.

So basically we need to write too much code and maintain it. And every piece of boilerplate in your code will eventually come back to haunt you.

But why is this so? This seems like a very common problem but all the available solutions are either too specific or composition of multiple libraries.

To solve this problem, we can just create a react-hook that handles the state and resolves the promise in a declarative and abstract way. We will just pass it a factory that returns a promise and it will execute it whenever we tell it to. If we want to cache the response then we just tell it where to cache it and DONE. You are now caching your response without much-added code.
Boom… Magic….  how….?

Here’s how it works.

Promise to cache your Data

I created an open-source library name called use-cached-promise for solving this problem.

You can use axios, fetch or whatever you want to create the promise factory, my library will handle it all. 

Currently, the Library has 2 types of cache functionality – Local-storage and In-Memory cache.

Install

yarn add use-cached-promise

How to Use?

If you want to call just API, and don’t want to cache your data.

import React, { Component } from 'react'
import useCachedPromise, { RESPONSE_STATUS } from "use-cached-promise";

const fetchIp = () =>
  fetch("https://httpbin.org/get")
    .then(r => r.json())
    .then(({ origin }) => origin);

const options = {
  cacheKey: "ipStore"
};

function IpInfo() {
  const { response, status } = useCachedPromise(fetchIp, options);

  return (
    <div className="App">
      <h1>{status === RESPONSE_STATUS.pending && "Loading..."}</h1>
      <h1>{status === RESPONSE_STATUS.success && `Your ip is ${response}`}</h1>
    </div>
  );
}

If you want to cache data permanently 

import React, { Component } from 'react'
import useCachedPromise, { RESPONSE_STATUS, LocalStorgeCacheAdapter } from "use-cached-promise";

const fetchIp = () =>
  fetch("https://httpbin.org/get")
    .then(r => r.json())
    .then(({ origin }) => origin);

const options = {
  cacheKey: "ipStore",
  cacheAdapter: new LocalStorgeCacheAdapter()
};

function IpInfo() {
  const { response, status } = useCachedPromise(fetchIp, options);

  return (
    <div className="App">
      <h1>{status === RESPONSE_STATUS.pending && "Loading..."}</h1>
      <h1>{status === RESPONSE_STATUS.success && `Your ip is ${response}`}</h1>
    </div>
  );
}

If you want to cache data with expiry time, don’t worry
just call the function with expiry time check below example

import React, { Component } from 'react'
import useCachedPromise, { RESPONSE_STATUS, LocalStorgeCacheAdapter } from "use-cached-promise";
const SECONDS = 1000;
const fetchIp = () =>
  fetch("https://httpbin.org/get")
    .then(r => r.json())
    .then(({ origin }) => origin);

const options = {
  cacheKey: "ipStore",
  cacheAdapter: new LocalStorgeCacheAdapter({ maxAge: 5 * SECONDS })
};

function IpInfo() {
  const { response, status } = useCachedPromise(fetchIp, options);

  return (
    <div className="App">
      <h1>{status === RESPONSE_STATUS.pending && "Loading..."}</h1>
      <h1>{status === RESPONSE_STATUS.success && `Your ip is ${response}`}</h1>
    </div>
  );
}

Response Status types

  • idle
  • pending
  • success
  • failure

Available Cache adapters

  • MemoryCacheAdapter
import { MemoryCacheAdapter } from 'use-cached-promise';
  • LocalStorageCacheAdapter
import { LocalStorageCacheAdapter } from 'use-cached-promise';

What the pipeline? 

Yes, We will add some feature in the next version. 
Like:

IndexedDB Cache support
The ability to cancel a promise that is in progress

Advantages

  • Less boilerplate so no need to write different files to call your API and cache data.
  • You can also create own custom cache adapters easily
  • It’s open-source 🙂
  • Most importantly, the library has an almost full test coverage 😀 😀