diff --git a/docs/demo/first-render.md b/docs/demo/first-render.md new file mode 100644 index 0000000..bad5bc3 --- /dev/null +++ b/docs/demo/first-render.md @@ -0,0 +1,8 @@ +--- +title: First Render +nav: + title: Demo + path: /demo +--- + + diff --git a/docs/examples/first-render.tsx b/docs/examples/first-render.tsx new file mode 100644 index 0000000..6b8a56c --- /dev/null +++ b/docs/examples/first-render.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import './basic.less'; +import Button from './components/Button'; + +const Demo = () => { + const renderStart = React.useRef(Date.now()); + const [renderTime, setRenderTime] = React.useState(0); + + React.useEffect(() => { + setRenderTime(Date.now() - renderStart.current); + }, []); + + return ( + <> +

Render Time: {renderTime}ms

+ {Array(10000) + .fill(1) + .map((_, key) => ( +
+ + + + +
+ ))} + + ); +}; + +export default function App() { + const [show, setShow] = React.useState(false); + + return ( +
+

默认情况下不会自动删除添加的样式

+ + + + {show && ( +
+ +
+ )} +
+ ); +} diff --git a/src/hooks/useGlobalCache.tsx b/src/hooks/useGlobalCache.tsx index 93ab6c3..9d0a6bf 100644 --- a/src/hooks/useGlobalCache.tsx +++ b/src/hooks/useGlobalCache.tsx @@ -13,6 +13,8 @@ export type ExtractStyle = ( }, ) => [order: number, styleId: string, style: string] | null; +const effectMap = new Map(); + export default function useGlobalCache( prefix: string, keyPath: KeyType[], @@ -84,7 +86,15 @@ export default function useGlobalCache( // which will clear cache on the first time. buildCache(([times, cache]) => { if (polyfill && times === 0) { - onCacheEffect?.(cacheContent); + if (!effectMap.has(fullPathStr)) { + onCacheEffect?.(cacheContent); + effectMap.set(fullPathStr, true); + + // 微任务清理缓存,可以认为是单次 batch render 中只触发一次 effect + Promise.resolve().then(() => { + effectMap.delete(fullPathStr); + }); + } } return [times + 1, cache]; }); @@ -97,6 +107,7 @@ export default function useGlobalCache( if (nextCount === 0) { // Always remove styles in useEffect callback register(() => { + effectMap.delete(fullPathStr); // 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. diff --git a/tests/animation.spec.tsx b/tests/animation.spec.tsx index cb69f52..98bc821 100644 --- a/tests/animation.spec.tsx +++ b/tests/animation.spec.tsx @@ -179,7 +179,7 @@ describe('animation', () => { }); }); - it('re-mount should not missing animation style', () => { + it('re-mount should not missing animation style', async () => { function genComp(cls: string) { return () => { const [token, hashId] = useCacheToken(theme, [baseToken], { @@ -210,6 +210,8 @@ describe('animation', () => { // Clean up document.head.innerHTML = ''; + await Promise.resolve(); + // Render again render(