From b600f4546da32d677dbfb9d657abc0fc69ad5ad8 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 29 Aug 2023 16:01:29 +0200 Subject: [PATCH 1/8] fix: use timeout instead of requestAnimationFrame for ssr --- .../react-utilities/src/hooks/useAnimationFrame.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts b/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts index f2453bd545a4d9..6aa7ff2a65a61b 100644 --- a/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts +++ b/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts @@ -1,3 +1,4 @@ +import { canUseDOM } from '../ssr/canUseDOM'; import { useBrowserTimer } from './useBrowserTimer'; /** @@ -9,6 +10,11 @@ import { useBrowserTimer } from './useBrowserTimer'; * @returns A pair of [requestAnimationFrame, cancelAnimationFrame] that are stable between renders. */ export function useAnimationFrame() { + const isDOM = canUseDOM(); + // TODO: figure it out a way to not call global.requestAnimationFrame and instead infer window from some context - return useBrowserTimer(requestAnimationFrame, cancelAnimationFrame); + const setAnimationFrame = isDOM ? requestAnimationFrame : setTimeout; + const clearAnimationFrame = isDOM ? cancelAnimationFrame : clearTimeout; + + return useBrowserTimer(setAnimationFrame, clearAnimationFrame); } From e297826693bda0e39abc645de85ee28e5052e10c Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 29 Aug 2023 16:02:05 +0200 Subject: [PATCH 2/8] fix: add missing changefile --- ...act-utilities-b4af8469-27c7-4b29-a6a9-13ef542e6779.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-react-utilities-b4af8469-27c7-4b29-a6a9-13ef542e6779.json diff --git a/change/@fluentui-react-utilities-b4af8469-27c7-4b29-a6a9-13ef542e6779.json b/change/@fluentui-react-utilities-b4af8469-27c7-4b29-a6a9-13ef542e6779.json new file mode 100644 index 00000000000000..0b0617f983224c --- /dev/null +++ b/change/@fluentui-react-utilities-b4af8469-27c7-4b29-a6a9-13ef542e6779.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: use timeout instead of requestAnimationFrame for ssr", + "packageName": "@fluentui/react-utilities", + "email": "marcosvmmoura@gmail.com", + "dependentChangeType": "patch" +} From 271a3d7a7ca19fa1da0c55afcde0a39b621d3b62 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Wed, 30 Aug 2023 14:26:27 +0200 Subject: [PATCH 3/8] test: add SSR tests for useAnimationFrame --- apps/ssr-tests-v9/package.json | 3 ++- .../stories/useAnimationFrame/Default.stories.tsx | 15 +++++++++++++++ .../stories/useAnimationFrame/index.stories.tsx | 5 +++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 apps/ssr-tests-v9/src/stories/useAnimationFrame/Default.stories.tsx create mode 100644 apps/ssr-tests-v9/src/stories/useAnimationFrame/index.stories.tsx diff --git a/apps/ssr-tests-v9/package.json b/apps/ssr-tests-v9/package.json index f20b350b3d84a1..20fc7dd9076842 100644 --- a/apps/ssr-tests-v9/package.json +++ b/apps/ssr-tests-v9/package.json @@ -18,7 +18,8 @@ "test-ssr": "test-ssr \"./stories/**/*.stories.tsx\"" }, "dependencies": { - "@fluentui/react-components": "*" + "@fluentui/react-components": "*", + "@fluentui/react-utilities": "*" }, "devDependencies": { "@fluentui/eslint-plugin": "*", diff --git a/apps/ssr-tests-v9/src/stories/useAnimationFrame/Default.stories.tsx b/apps/ssr-tests-v9/src/stories/useAnimationFrame/Default.stories.tsx new file mode 100644 index 00000000000000..1b6aad0dc53d80 --- /dev/null +++ b/apps/ssr-tests-v9/src/stories/useAnimationFrame/Default.stories.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import { useAnimationFrame } from '@fluentui/react-utilities'; + +export const DefaultOpen = () => { + const [setAnimationFrame, clearAnimationFrame] = useAnimationFrame(); + const [visible, setVisible] = React.useState(false); + + React.useEffect(() => { + setAnimationFrame(() => setVisible(true)); + + return () => clearAnimationFrame(); + }, [setAnimationFrame]); + + return visible ?
: null; +}; diff --git a/apps/ssr-tests-v9/src/stories/useAnimationFrame/index.stories.tsx b/apps/ssr-tests-v9/src/stories/useAnimationFrame/index.stories.tsx new file mode 100644 index 00000000000000..d07e1b0a7f2e51 --- /dev/null +++ b/apps/ssr-tests-v9/src/stories/useAnimationFrame/index.stories.tsx @@ -0,0 +1,5 @@ +export { DefaultOpen } from './Default.stories'; + +export default { + title: 'Utilities/useAnimationFrame', +}; From f4d0c1705ca847584f45e1fb5c5ff79486ff09cc Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Wed, 30 Aug 2023 14:35:35 +0200 Subject: [PATCH 4/8] fix: add blank content --- .../src/stories/useAnimationFrame/Default.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ssr-tests-v9/src/stories/useAnimationFrame/Default.stories.tsx b/apps/ssr-tests-v9/src/stories/useAnimationFrame/Default.stories.tsx index 1b6aad0dc53d80..8c85957eaa8807 100644 --- a/apps/ssr-tests-v9/src/stories/useAnimationFrame/Default.stories.tsx +++ b/apps/ssr-tests-v9/src/stories/useAnimationFrame/Default.stories.tsx @@ -11,5 +11,5 @@ export const DefaultOpen = () => { return () => clearAnimationFrame(); }, [setAnimationFrame]); - return visible ?
: null; + return visible ?
Test the renderization
: null; }; From ae491d7c44017168b53c800373e92881c6993ab2 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Wed, 30 Aug 2023 14:48:55 +0200 Subject: [PATCH 5/8] fix: move stories to a dedicated folder --- .../stories/{useAnimationFrame => Utilitites}/index.stories.tsx | 2 +- .../useAnimationFrame.stories.tsx} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename apps/ssr-tests-v9/src/stories/{useAnimationFrame => Utilitites}/index.stories.tsx (52%) rename apps/ssr-tests-v9/src/stories/{useAnimationFrame/Default.stories.tsx => Utilitites/useAnimationFrame.stories.tsx} (92%) diff --git a/apps/ssr-tests-v9/src/stories/useAnimationFrame/index.stories.tsx b/apps/ssr-tests-v9/src/stories/Utilitites/index.stories.tsx similarity index 52% rename from apps/ssr-tests-v9/src/stories/useAnimationFrame/index.stories.tsx rename to apps/ssr-tests-v9/src/stories/Utilitites/index.stories.tsx index d07e1b0a7f2e51..d06c147c868846 100644 --- a/apps/ssr-tests-v9/src/stories/useAnimationFrame/index.stories.tsx +++ b/apps/ssr-tests-v9/src/stories/Utilitites/index.stories.tsx @@ -1,4 +1,4 @@ -export { DefaultOpen } from './Default.stories'; +export { Default } from './useAnimationFrame.stories'; export default { title: 'Utilities/useAnimationFrame', diff --git a/apps/ssr-tests-v9/src/stories/useAnimationFrame/Default.stories.tsx b/apps/ssr-tests-v9/src/stories/Utilitites/useAnimationFrame.stories.tsx similarity index 92% rename from apps/ssr-tests-v9/src/stories/useAnimationFrame/Default.stories.tsx rename to apps/ssr-tests-v9/src/stories/Utilitites/useAnimationFrame.stories.tsx index 8c85957eaa8807..aba7f94b251a82 100644 --- a/apps/ssr-tests-v9/src/stories/useAnimationFrame/Default.stories.tsx +++ b/apps/ssr-tests-v9/src/stories/Utilitites/useAnimationFrame.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { useAnimationFrame } from '@fluentui/react-utilities'; -export const DefaultOpen = () => { +export const Default = () => { const [setAnimationFrame, clearAnimationFrame] = useAnimationFrame(); const [visible, setVisible] = React.useState(false); From 63893a5487aac46f5c30a239464c9a745b55e05e Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Wed, 30 Aug 2023 14:49:13 +0200 Subject: [PATCH 6/8] fix: use noop instead of timeout --- .../react-utilities/src/hooks/useAnimationFrame.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts b/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts index 6aa7ff2a65a61b..ed535cfec22fb4 100644 --- a/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts +++ b/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts @@ -11,10 +11,11 @@ import { useBrowserTimer } from './useBrowserTimer'; */ export function useAnimationFrame() { const isDOM = canUseDOM(); + const noop = () => 0; // TODO: figure it out a way to not call global.requestAnimationFrame and instead infer window from some context - const setAnimationFrame = isDOM ? requestAnimationFrame : setTimeout; - const clearAnimationFrame = isDOM ? cancelAnimationFrame : clearTimeout; + const setAnimationFrame = isDOM ? requestAnimationFrame : noop; + const clearAnimationFrame = isDOM ? cancelAnimationFrame : noop; return useBrowserTimer(setAnimationFrame, clearAnimationFrame); } From c9950fe98c903223f6be5e9d8fc7e8ef4c66945a Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Wed, 30 Aug 2023 14:56:25 +0200 Subject: [PATCH 7/8] fix: move fn to outside scope of hook --- .../react-utilities/src/hooks/useAnimationFrame.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts b/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts index ed535cfec22fb4..e4af08c1b579c8 100644 --- a/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts +++ b/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts @@ -1,6 +1,8 @@ import { canUseDOM } from '../ssr/canUseDOM'; import { useBrowserTimer } from './useBrowserTimer'; +const noop = () => 0; + /** * @internal * Helper to manage a browser requestAnimationFrame. @@ -11,7 +13,6 @@ import { useBrowserTimer } from './useBrowserTimer'; */ export function useAnimationFrame() { const isDOM = canUseDOM(); - const noop = () => 0; // TODO: figure it out a way to not call global.requestAnimationFrame and instead infer window from some context const setAnimationFrame = isDOM ? requestAnimationFrame : noop; From 9d01ebee4771c77aafe5aa76428733bdb5eb198d Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Wed, 30 Aug 2023 14:59:23 +0200 Subject: [PATCH 8/8] fix: call callback passed by requestAnimationFrame --- .../react-utilities/src/hooks/useAnimationFrame.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts b/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts index e4af08c1b579c8..a95e862aad7395 100644 --- a/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts +++ b/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts @@ -1,7 +1,11 @@ import { canUseDOM } from '../ssr/canUseDOM'; import { useBrowserTimer } from './useBrowserTimer'; -const noop = () => 0; +const setAnimationFrameNoop = (callback: FrameRequestCallback) => { + callback(0); + return 0; +}; +const cancelAnimationFrameNoop = (handle: number) => handle; /** * @internal @@ -15,8 +19,8 @@ export function useAnimationFrame() { const isDOM = canUseDOM(); // TODO: figure it out a way to not call global.requestAnimationFrame and instead infer window from some context - const setAnimationFrame = isDOM ? requestAnimationFrame : noop; - const clearAnimationFrame = isDOM ? cancelAnimationFrame : noop; + const setAnimationFrame = isDOM ? requestAnimationFrame : setAnimationFrameNoop; + const clearAnimationFrame = isDOM ? cancelAnimationFrame : cancelAnimationFrameNoop; return useBrowserTimer(setAnimationFrame, clearAnimationFrame); }