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(