All files cacheEnhancer.ts

60.78% Statements 31/51
53.13% Branches 17/32
50% Functions 6/12
62.22% Lines 28/45
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92  1x               1x 1x 1x 1x                     1x 4x 4x 4x   4x 5x 5x     5x   4x       4x 4x   4x         4x 4x               4x   2x 2x 1x   1x 1x 1x 1x               1x                                    
import { DEFAULT_KEY } from "./constants";
import { INVALIDATE_CACHE, InvalidateCacheAction } from "./actions";

export interface Store {
	[x: string]: any,
	replaceReducer: (reducer: () => State) => Function
}

export interface State {
	DEFAULT_KEY?: number | null | undefined
	[x: string]: any,
}

export interface CacheEnhancerConfig {
	log?: boolean
}
 
const logResult = (name: string, array: string[]): void => {
	console.log("redux-cache: %s: %s", name, array.join(", ") || "none found");
};
 
/**
 * // TODO: Add info here
 * 
 * @param {string[]} reducersToInvalidate - List of reducers to invalidate
 * @param {object} currentState - The current and already reduced state.
 * @param {object} [config={}] - Configuration options
 * @param {boolean} [config.log=false] - Whether or not to output log information. Useful for debugging.
 */
export const updateState = (reducersToInvalidate, currentState, config) => {
	const {I log = false } = config;
	const newState = { ...currentState };
	const stateKeys = Object.keys(newState);
 
	// We filter to those reducers which exist in the application state tree
	conIst matchedReducers = reducersToInvalidate.filter(reducerKey => {
		const matched = (stateKeys.indexOf(reducerKey) !== -1);
		if (!matched && log) { console.log("redux-cache: did not match %s reducer to state tree", reducerKey); }
		return matched;
	});
	if (log) { logResult("matchedReducers", matchedReducers); }
 
	// IWe filter those existing reducers down to those which actually have a the cache key.
	const cacheEnabledReducers = matchedReducers.filter(reducerKey => {
		return newState && newState[reducerKey] && newState[reducerKey][DEFAULT_KEY];
	});
	if (log) { logResult("cacheEnabledReducers", cacheEnabledReducers); }
 
	// IWe are invalidating the cached reducers by setting the value for the cache key to null.
	// Don't fret -- they'll get a new and improved value for the cache key again when the successful action comes through.
	cacheEnabledReducers.forEach(reducerKey => { newState[reducerKey][DEFAULT_KEY] = null; });
	if (log) {
		if (cacheEnabledReducers.length > 0) {
			console.log("redux-cache: Set %s to null for following reducers: %s", DEFAULT_KEY, cacheEnabledReducers.join(", "));
		} else {
			console.log("redux-cache: No cached reducers to update");
		}
	}
 
	return newState;
};
 
export const liftReducer = (reducer, config) => (state, action) => {
	if (action.type !== INVALIDATE_CACHE) {
		return reducer(state, action);
	}
 
	const reducersToInvalidate = action.payload && action.payload.reducers || [];
	const currentState = reducer(state, action);
	const newState = updateState(reducersToInvalidate, currentState, config);
 
	return newState;
}

/**
 * // TODO: add info here
 * 
 * @param config 
 * @returns {Object} - returns the enhanced store
 */
const cacheEnhancer = (config: CacheEnhancerConfig = {}) => {
	return (createStore) => (rootReducer, initialState, enhancer) => {
		const store = createStore(liftReducer(rootReducer, config), initialState, enhancer);
 
		return { 
			...store,
			replaceReducer: (reducer) => {
				return store.replaceReducer(liftReducer(reducer, config));
			}
		}
	}
}