/* 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 { ResourceBound, ResourceState } from "./core"; import type { ResourceQuery, QueryCacheHandler, QueryContext, QueryResult } from "./base-query"; import { strictEqual, shallowEqual } from "./compare"; export type WeakArgsBound = | $ReadOnly<{ [string]: mixed }> | $ReadOnlyArray; export type ShallowArgsBound = | $ReadOnly<{ [string]: mixed }> | $ReadOnlyArray; /** * A query 'cache' function that uses the identity of the arguments object to * cache data for the query itself. */ export function queryCacheWeak< R: ResourceBound, Args: WeakArgsBound, Mapped, Reduced >( handler: QueryCacheHandler ): ResourceQuery { const cache = new WeakMap(); return makeCacheFunction({ handler, // The WeakMap will only return entries for the exact object, // so there is no need to compare at all. compareArgs: () => true, getEntry: args => cache.get(args) || null, setEntry: (args, entry) => { cache.set(args, entry); } }); } /** * A query 'cache' function that uses shallow comparison to cache the most * recent calculated result based on the value of the argument. */ export function queryCacheShallow< R: ResourceBound, // We require args to be an object here because if you're using a primitive // then you should be using queryCacheStrict instead. Args: ShallowArgsBound, Mapped, Reduced >( handler: QueryCacheHandler ): ResourceQuery { let latestEntry = null; return makeCacheFunction({ handler, compareArgs: shallowEqual, getEntry: () => latestEntry, setEntry: (args, entry) => { latestEntry = entry; } }); } /** * A query 'cache' function that uses strict comparison to cache the most * recent calculated result based on the value of the argument. */ export function queryCacheStrict( handler: QueryCacheHandler ): ResourceQuery { let latestEntry = null; return makeCacheFunction({ handler, compareArgs: strictEqual, getEntry: () => latestEntry, setEntry: (args, entry) => { latestEntry = entry; } }); } type CacheEntry = { context: QueryContext, state: ResourceState, result: QueryResult }; type CacheFunctionInfo = {| // The handler to call when the args or the state are different from // those in the entry for the arguments. handler: QueryCacheHandler, // Compare two sets of arguments to decide whether or not they should be // treated as the same set of arguments from the standpoint of caching. compareArgs: (a: Args, b: Args) => boolean, getEntry: (args: Args) => CacheEntry | null, setEntry: (args: Args, entry: CacheEntry) => void |}; function makeCacheFunction( info: CacheFunctionInfo ): ResourceQuery { const { handler, compareArgs, getEntry, setEntry } = info; return (state, args: Args) => { let entry = getEntry(args); const sameArgs = !!entry && compareArgs(entry.context.args, args); const sameState = !!entry && entry.state === state; if (!entry || !sameArgs || !sameState) { const context = !entry || !sameArgs ? { args, identMap: new WeakMap() } : entry.context; const result = handler(state, context, entry ? entry.result : null); if (entry) { entry.context = context; entry.state = state; entry.result = result; } else { entry = { context, state, result }; setEntry(args, entry); } } return entry.result.reduced; }; }