From 8658aafe6787fccc64f6b41e093933f79aafa3a2 Mon Sep 17 00:00:00 2001 From: HaixingOoO <974758671@qq.com> Date: Mon, 27 Nov 2023 20:11:33 +0800 Subject: [PATCH] perf(statistic): perf statistic --- src/hooks/useIsFirstRender.ts | 14 +++++++ src/statistic/Statistic.tsx | 43 ++++++++------------ src/statistic/__tests__/statistic.test.tsx | 46 +++++++++++++++------- src/statistic/_example/combination.jsx | 19 +++++---- src/statistic/_example/loading.jsx | 2 +- src/statistic/_example/slot.jsx | 3 +- 6 files changed, 76 insertions(+), 51 deletions(-) create mode 100644 src/hooks/useIsFirstRender.ts diff --git a/src/hooks/useIsFirstRender.ts b/src/hooks/useIsFirstRender.ts new file mode 100644 index 0000000000..d9870909ec --- /dev/null +++ b/src/hooks/useIsFirstRender.ts @@ -0,0 +1,14 @@ +import { useRef } from 'react'; + +const useIsFirstRender = () => { + const isFirstRenderRef = useRef(true); + + if (isFirstRenderRef.current) { + isFirstRenderRef.current = false; + return true; + } + + return isFirstRenderRef.current; +}; + +export default useIsFirstRender; diff --git a/src/statistic/Statistic.tsx b/src/statistic/Statistic.tsx index cd7fbe31b7..24774f9291 100644 --- a/src/statistic/Statistic.tsx +++ b/src/statistic/Statistic.tsx @@ -11,6 +11,7 @@ import { StyledProps } from '../common'; import useConfig from '../hooks/useConfig'; import useGlobalIcon from '../hooks/useGlobalIcon'; import useDefaultProps from '../hooks/useDefaultProps'; +import useIsFirstRender from '../hooks/useIsFirstRender'; import Skeleton from '../skeleton'; import Tween from '../_common/js/statistic/tween'; @@ -51,8 +52,8 @@ const Statistic = forwardRef((props, ref) => { */ const [innerValue, setInnerValue] = useState(animation?.valueFrom ?? value); const numberValue = useMemo(() => (isNumber(value) ? value : 0), [value]); - const tween = useRef(null); + const isFirstRender = useIsFirstRender(); const start = (from: number = animation?.valueFrom ?? 0, to: number = numberValue) => { if (from !== to) { @@ -63,7 +64,7 @@ const Statistic = forwardRef((props, ref) => { to: { value: to, }, - duration: props.animation.duration, + duration: animation?.duration, onUpdate: (keys) => { setInnerValue(keys.value); }, @@ -76,11 +77,10 @@ const Statistic = forwardRef((props, ref) => { }; const formatValue = useMemo(() => { - // eslint-disable-next-line no-underscore-dangle - let _value: number | undefined | string = innerValue; + let formatInnerValue: number | string | undefined = innerValue; if (isFunction(format)) { - return format(_value); + return format(formatInnerValue); } const options = { minimumFractionDigits: decimalPlaces || 0, @@ -88,9 +88,9 @@ const Statistic = forwardRef((props, ref) => { useGrouping: !!separator, }; // replace的替换的方案仅能应对大部分地区 - _value = _value.toLocaleString(undefined, options).replace(/,|,/g, separator); + formatInnerValue = formatInnerValue.toLocaleString(undefined, options).replace(/,|,/g, separator); - return _value; + return formatInnerValue; }, [innerValue, decimalPlaces, separator, format]); const valueStyle = useMemo( @@ -102,37 +102,28 @@ const Statistic = forwardRef((props, ref) => { ); useEffect(() => { - animation && animationStart && start(); - return () => { - if (tween.current) { - tween.current.stop(); - } - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + // 第一次渲染不执行,否则导致初始formValue失效 + console.log('isFirstRender', isFirstRender); + if (isFirstRender) return; - useEffect(() => { - animationStart && animation && !tween.current && start(); + setInnerValue(value); + + animationStart && animation && start(); return () => { if (tween.current) { tween.current.stop(); } }; + // eslint-disable-next-line react-hooks/exhaustive-deps - }, [animationStart]); + }, [value]); useEffect(() => { - if (tween.current) { - tween.current?.stop(); - tween.current = null; - } - setInnerValue(value); - - animationStart && animation && start(); + animationStart && animation && !tween.current && start(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [value]); + }, [animationStart]); useImperativeHandle(ref, () => ({ start, diff --git a/src/statistic/__tests__/statistic.test.tsx b/src/statistic/__tests__/statistic.test.tsx index ebe19e83a0..928d23f92d 100644 --- a/src/statistic/__tests__/statistic.test.tsx +++ b/src/statistic/__tests__/statistic.test.tsx @@ -1,18 +1,8 @@ import React from 'react'; -import { render, fireEvent, waitFor } from '@test/utils'; -import { vi } from 'vitest'; +import { render, fireEvent, mockDelay } from '@test/utils'; import { ArrowTriangleDownFilledIcon, ArrowTriangleUpFilledIcon } from 'tdesign-icons-react'; import Statistic from '../index'; -beforeEach(() => { - vi.useFakeTimers({ shouldAdvanceTime: true }); -}); - -afterEach(() => { - vi.runOnlyPendingTimers(); - vi.useRealTimers(); -}); - describe('Statistic 组件测试', () => { /** * props @@ -117,10 +107,36 @@ describe('Statistic 组件测试', () => { fireEvent.click(document.querySelector('#button')); - vi.advanceTimersByTime(2000); + await mockDelay(2000); - await waitFor(() => { - expect(document.querySelector('.t-statistic-content-value')).toHaveTextContent('82.76'); - }); + expect(document.querySelector('.t-statistic-content-value')).toHaveTextContent('82.76'); + }); + + /** + * not animation config display value + */ + test('not animation', async () => { + render( +value.toFixed(2)} />); + + expect(document.querySelector('.t-statistic-content-value')).toHaveTextContent('82.76'); + }); + + /** + * have animation config display valueFrom + */ + test('not animation', async () => { + render( + +value.toFixed(2)} + />, + ); + + expect(document.querySelector('.t-statistic-content-value')).toHaveTextContent('0'); }); }); diff --git a/src/statistic/_example/combination.jsx b/src/statistic/_example/combination.jsx index 8201e69bd9..1153f7ebb7 100644 --- a/src/statistic/_example/combination.jsx +++ b/src/statistic/_example/combination.jsx @@ -32,16 +32,19 @@ const CombinationStatistic = () => { color="red" extra={ - - the day before - + +
the day before
+ 9% -
- - last week - +
+ +
last week
+ 9% - +
} >
diff --git a/src/statistic/_example/loading.jsx b/src/statistic/_example/loading.jsx index 8df40981ea..20a69b8556 100644 --- a/src/statistic/_example/loading.jsx +++ b/src/statistic/_example/loading.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { Space, Switch, Statistic } from 'tdesign-react'; const LoadingStatistic = () => { - const [loading, setLoading] = React.useState(false); + const [loading, setLoading] = React.useState(true); return ( setLoading(value)} size="large" /> diff --git a/src/statistic/_example/slot.jsx b/src/statistic/_example/slot.jsx index b395ff6ac8..42affffcbc 100644 --- a/src/statistic/_example/slot.jsx +++ b/src/statistic/_example/slot.jsx @@ -3,13 +3,14 @@ import { Space, Statistic } from 'tdesign-react'; import { ControlPlatformIcon, ArrowTriangleDownFilledIcon } from 'tdesign-icons-react'; const SlotStatistic = () => ( - + }> } >