JavaScript
promise
React
redux
axios

ReduxでAPIから取得したデータを保持するのが嫌になったのでaxiosで保持してみた

More than 1 year has passed since last update.

ReduxのGlobalStateでAPIのデータを保持するか否か

Reduxではredux-thunkやredux-sagaを用いてaction内でAPIリクエストした結果をGlobalStateに保持するのがお約束になっていますが、そもそも

action作成

reducerにcase追加

containerのmapStateToPropsで受け取る

というプロセスが非常にめんどくさい。
しかもGlobalStateにAPIデータが保持されているかどうかで分岐もさせないといけない。

それならaxiosのPromiseをキャッシュしておいて、2度目以降同じaxios.getリクエストをした場合にそのキャッシュを返せばいんじゃね?
ということでaxios.getをキャッシュする仕組みを作ってみました。

注意点として、この中でキャッシュと呼んでいるものは永続的なものでなくリロードすると消えるものです。

使用するモジュール

・Webpack (3.0.0)
・axios (0.16.2)

コード

URLとクエリパラメータが完全に一致するキャッシュが存在する場合にのみキャッシュを返します。
また allowedCaches.js に指定したパターンに一致するリクエストのみキャッシュします。

./src/axios-cache/index.js
import 'babel-polyfill';
import allowedCaches from './allowedCaches';

/**
 * keyをソートして返却
 * @param {object} object
 * @return {object}
 */
function objectSort(object) {
  let array = [];
  for (let key in object) {
    if (object.hasOwnProperty(key)) {
      array.push(key);
    }
  }

  array.sort();

  let sorted = {};
  for (let i = 0; i < array.length; i++) {
    sorted[array[i]] = object[array[i]];
  }

  return sorted;
}

/**
 * @param {object} params
 * @return {string}
 */
function createQuery(params) {
  params = objectSort(params);

  let ary = [];
  Object.keys(params).forEach(function(key) {
    ary.push(`${key}=${encodeURIComponent(params[key])}`);
  });
  return ary.join('&');
}

function match(url) {
  for (let i=0; i < allowedCaches.length; i++) {
    const allowedUrl = allowedCaches[i];
    if (url.match(new RegExp(allowedUrl))) {
      return true;
    }
  }
  return false;
}

/**
 * @param {Axios} axios - axios instance
 */
export function cachingGet(axios) {
  const get = axios.get;
  const cache = new Map();

  axios.get = function cachedGet(url, config) {
    const key = (config && config.params) ? `${url}?${createQuery(config.params)}` : url;
    if (cache.has(key)) {
      return cache.get(key);
    } else {
      const request = get.apply(axios, arguments);
      if (match(url)) {
        cache.set(key, request);
      }
      return request;
    }
  };
}

./src/axios-cache/allowedCaches.js
export default [
  '/api/users.*' // 正規表現
];

使い方

./src/main.js
import axios from 'axios'
import { cachingGet } from './axios-cache'

const axiosInstance = axios.create();
cachingGet(axiosInstance);

axiosInstance.get('/api/users');
// 2度目以降はキャッシュしたpromiseインスタンスが返却される
axiosInstance.get('/api/users');

// クエリパラメータまで一致しない場合、別のリクエストとみなす
axiosInstance.get('/api/users?a=b');
axiosInstance.get('/api/users?a=c');