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');