/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at . */ // @flow import type { ThunkArgs } from "../actions/types"; export type MemoizedAction< Args, Result > = Args => ThunkArgs => Promise; type MemoizableActionParams = { exitEarly?: (args: Args, thunkArgs: ThunkArgs) => boolean, hasValue: (args: Args, thunkArgs: ThunkArgs) => boolean, getValue: (args: Args, thunkArgs: ThunkArgs) => Result, createKey: (args: Args, thunkArgs: ThunkArgs) => string, action: (args: Args, thunkArgs: ThunkArgs) => Promise }; /* * memoizableActon is a utility for actions that should only be performed * once per key. It is useful for loading sources, parsing symbols ... * * @exitEarly - if true, do not attempt to perform the action * @hasValue - checks to see if the result is in the redux store * @getValue - gets the result from the redux store * @createKey - creates a key for the requests map * @action - kicks off the async work for the action * * * For Example * * export const setItem = memoizeableAction( * "setItem", * { * hasValue: ({ a }, { getState }) => hasItem(getState(), a), * getValue: ({ a }, { getState }) => getItem(getState(), a), * createKey: ({ a }) => a, * action: ({ a }, thunkArgs) => doSetItem(a, thunkArgs) * } * ); * */ export function memoizeableAction( name: string, { hasValue, getValue, createKey, action, exitEarly }: MemoizableActionParams ): MemoizedAction { const requests = new Map(); return args => async (thunkArgs: ThunkArgs) => { if (exitEarly && exitEarly(args, thunkArgs)) { return; } if (hasValue(args, thunkArgs)) { return getValue(args, thunkArgs); } const key = createKey(args, thunkArgs); if (!requests.has(key)) { requests.set( key, (async () => { try { await action(args, thunkArgs); } catch (e) { console.warn(`Action ${name} had an exception:`, e); } finally { requests.delete(key); } })() ); } await requests.get(key); return getValue(args, thunkArgs); }; }