From 44bf198006a2f50d2b4bd757bdcac03778450916 Mon Sep 17 00:00:00 2001 From: awmleer Date: Tue, 31 May 2022 11:02:36 +0800 Subject: [PATCH] refactor: add `useMotionReduced` to prevent infinite animation calculation in react-spring --- package.json | 4 +++- src/components/spin-loading/spin-loading.tsx | 13 ++++++------- src/utils/reduce-and-restore-motion.ts | 20 ++++++++++++++++++++ yarn.lock | 18 ++++++++++++++++++ 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index f982d4135b..def6c5a0a0 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "rc-field-form": "^1.26.6", "react-is": "^17.0.2", "staged-components": "^1.1.3", - "tslib": "^2.4.0" + "tslib": "^2.4.0", + "use-sync-external-store": "^1.1.0" }, "devDependencies": { "@babel/cli": "^7.17.10", @@ -56,6 +57,7 @@ "@types/react-is": "^17.0.3", "@types/react-virtualized": "^9.21.21", "@types/resize-observer-browser": "^0.1.7", + "@types/use-sync-external-store": "^0.0.3", "@typescript-eslint/eslint-plugin": "^5.26.0", "@typescript-eslint/parser": "^5.26.0", "babel-loader": "^8.2.5", diff --git a/src/components/spin-loading/spin-loading.tsx b/src/components/spin-loading/spin-loading.tsx index 6f71002b85..940ea3c11a 100644 --- a/src/components/spin-loading/spin-loading.tsx +++ b/src/components/spin-loading/spin-loading.tsx @@ -2,7 +2,7 @@ import React, { memo } from 'react' import { NativeProps, withNativeProps } from '../../utils/native-props' import { mergeProps } from '../../utils/with-default-props' import { useSpring, animated } from '@react-spring/web' -import { isMotionReduced } from '../../utils/reduce-and-restore-motion' +import { useMotionReduced } from '../../utils/reduce-and-restore-motion' const classPrefix = 'adm-spin-loading' @@ -24,13 +24,12 @@ const circumference = 15 * 3.14159265358979 * 2 export const SpinLoading = memo(p => { const props = mergeProps(defaultProps, p) - + const motionReduced = useMotionReduced() const { percent } = useSpring({ - loop: isMotionReduced() - ? false - : { - reverse: true, - }, + cancel: motionReduced, + loop: { + reverse: true, + }, from: { percent: 80, }, diff --git a/src/utils/reduce-and-restore-motion.ts b/src/utils/reduce-and-restore-motion.ts index bce356182f..24ace5bbe0 100644 --- a/src/utils/reduce-and-restore-motion.ts +++ b/src/utils/reduce-and-restore-motion.ts @@ -1,9 +1,19 @@ import { Globals } from '@react-spring/web' +import { useSyncExternalStore } from 'use-sync-external-store/shim' let reduced = false +const subscribers = new Set<() => void>() + +function notify() { + subscribers.forEach(subscriber => { + subscriber() + }) +} + export function reduceMotion() { reduced = true + notify() Globals.assign({ skipAnimation: true, }) @@ -11,6 +21,7 @@ export function reduceMotion() { export function restoreMotion() { reduced = false + notify() Globals.assign({ skipAnimation: false, }) @@ -19,3 +30,12 @@ export function restoreMotion() { export function isMotionReduced() { return reduced } + +export function useMotionReduced() { + return useSyncExternalStore(onStoreChange => { + subscribers.add(onStoreChange) + return () => { + subscribers.delete(onStoreChange) + } + }, isMotionReduced) +} diff --git a/yarn.lock b/yarn.lock index 4f96ab50f1..a215ad5c62 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3498,6 +3498,13 @@ __metadata: languageName: node linkType: hard +"@types/use-sync-external-store@npm:^0.0.3": + version: 0.0.3 + resolution: "@types/use-sync-external-store@npm:0.0.3" + checksum: 161ddb8eec5dbe7279ac971531217e9af6b99f7783213566d2b502e2e2378ea19cf5e5ea4595039d730aa79d3d35c6567d48599f69773a02ffcff1776ec2a44e + languageName: node + linkType: hard + "@types/vinyl@npm:^2.0.4": version: 2.0.6 resolution: "@types/vinyl@npm:2.0.6" @@ -4487,6 +4494,7 @@ __metadata: "@types/react-is": ^17.0.3 "@types/react-virtualized": ^9.21.21 "@types/resize-observer-browser": ^0.1.7 + "@types/use-sync-external-store": ^0.0.3 "@typescript-eslint/eslint-plugin": ^5.26.0 "@typescript-eslint/parser": ^5.26.0 "@use-gesture/react": 10.2.15 @@ -4543,6 +4551,7 @@ __metadata: ts-node: 10.8.0 tslib: ^2.4.0 typescript: ~4.6.4 + use-sync-external-store: ^1.1.0 vite: ^2.9.9 webpack: ^5.72.1 webpack-bundle-analyzer: ^4.5.0 @@ -16600,6 +16609,15 @@ __metadata: languageName: node linkType: hard +"use-sync-external-store@npm:^1.1.0": + version: 1.1.0 + resolution: "use-sync-external-store@npm:1.1.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 8993a0b642f91d7fcdbb02b7b3ac984bd3af4769686f38291fe7fcfe73dfb73d6c64d20dfb7e5e7fbf5a6da8f5392d6f8e5b00c243a04975595946e82c02b883 + languageName: node + linkType: hard + "use@npm:^3.1.0": version: 3.1.1 resolution: "use@npm:3.1.1"