diff --git a/src/Cache.ts b/src/Cache.ts index 8644355..c0e1b6d 100644 --- a/src/Cache.ts +++ b/src/Cache.ts @@ -3,6 +3,11 @@ type ValueType = [number, any]; // [times, realValue] const SPLIT = '%'; +/** Connect key with `SPLIT` */ +export function pathKey(keys: KeyType[]) { + return keys.join(SPLIT); +} + class Entity { instanceId: string; constructor(instanceId: string) { @@ -13,21 +18,33 @@ class Entity { cache = new Map(); get(keys: KeyType[]): ValueType | null { - return this.cache.get(keys.join(SPLIT)) || null; + return this.opGet(pathKey(keys)); + } + + /** A fast get cache with `get` concat. */ + opGet(keyPathStr: string): ValueType | null { + return this.cache.get(keyPathStr) || null; } update( keys: KeyType[], valueFn: (origin: ValueType | null) => ValueType | null, ) { - const path = keys.join(SPLIT); - const prevValue = this.cache.get(path)!; + return this.opUpdate(pathKey(keys), valueFn); + } + + /** A fast get cache with `get` concat. */ + opUpdate( + keyPathStr: string, + valueFn: (origin: ValueType | null) => ValueType | null, + ) { + const prevValue = this.cache.get(keyPathStr)!; const nextValue = valueFn(prevValue); if (nextValue === null) { - this.cache.delete(path); + this.cache.delete(keyPathStr); } else { - this.cache.set(path, nextValue); + this.cache.set(keyPathStr, nextValue); } } } diff --git a/src/hooks/useGlobalCache.tsx b/src/hooks/useGlobalCache.tsx index ed5bff7..93ab6c3 100644 --- a/src/hooks/useGlobalCache.tsx +++ b/src/hooks/useGlobalCache.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import type { KeyType } from '../Cache'; +import { pathKey, type KeyType } from '../Cache'; import StyleContext from '../StyleContext'; import useCompatibleInsertionEffect from './useCompatibleInsertionEffect'; import useEffectCleanupRegister from './useEffectCleanupRegister'; @@ -23,16 +23,16 @@ export default function useGlobalCache( ): CacheType { const { cache: globalCache } = React.useContext(StyleContext); const fullPath = [prefix, ...keyPath]; - const deps = fullPath.join('_'); + const fullPathStr = pathKey(fullPath); - const register = useEffectCleanupRegister([deps]); + const register = useEffectCleanupRegister([fullPathStr]); const HMRUpdate = useHMR(); type UpdaterArgs = [times: number, cache: CacheType]; const buildCache = (updater?: (data: UpdaterArgs) => UpdaterArgs) => { - globalCache.update(fullPath, (prevCache) => { + globalCache.opUpdate(fullPathStr, (prevCache) => { const [times = 0, cache] = prevCache || [undefined, undefined]; // HMR should always ignore cache since developer may change it @@ -57,18 +57,18 @@ export default function useGlobalCache( buildCache(); }, /* eslint-disable react-hooks/exhaustive-deps */ - [deps], + [fullPathStr], /* eslint-enable */ ); - let cacheEntity = globalCache.get(fullPath); + let cacheEntity = globalCache.opGet(fullPathStr); // HMR clean the cache but not trigger `useMemo` again // Let's fallback of this // ref https://github.com/ant-design/cssinjs/issues/127 if (process.env.NODE_ENV !== 'production' && !cacheEntity) { buildCache(); - cacheEntity = globalCache.get(fullPath); + cacheEntity = globalCache.opGet(fullPathStr); } const cacheContent = cacheEntity![1]; @@ -90,7 +90,7 @@ export default function useGlobalCache( }); return () => { - globalCache.update(fullPath, (prevCache) => { + globalCache.opUpdate(fullPathStr, (prevCache) => { const [times = 0, cache] = prevCache || []; const nextCount = times - 1; @@ -100,7 +100,7 @@ export default function useGlobalCache( // With polyfill, registered callback will always be called synchronously // But without polyfill, it will be called in effect clean up, // And by that time this cache is cleaned up. - if (polyfill || !globalCache.get(fullPath)) { + if (polyfill || !globalCache.opGet(fullPathStr)) { onCacheRemove?.(cache, false); } }); @@ -111,7 +111,7 @@ export default function useGlobalCache( }); }; }, - [deps], + [fullPathStr], ); return cacheContent;