Skip to content

Commit 5ed3463

Browse files
afc163zombieJ
andauthored
perf: fix Maximum update depth exceeded caused by inkStyle shaking (#820)
Co-authored-by: 二货机器人 <[email protected]>
1 parent c7bd830 commit 5ed3463

File tree

3 files changed

+68
-15
lines changed

3 files changed

+68
-15
lines changed

src/hooks/useIndicator.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,25 @@ const useIndicator = (options: UseIndicatorOptions) => {
7676

7777
cleanInkBarRaf();
7878
inkBarRafRef.current = raf(() => {
79-
setInkStyle(newInkStyle);
79+
// Avoid jitter caused by tiny numerical differences
80+
// fix https://github.com/ant-design/ant-design/issues/53378
81+
const isEqual =
82+
inkStyle &&
83+
newInkStyle &&
84+
Object.keys(newInkStyle).every(key => {
85+
const newValue = newInkStyle[key];
86+
const oldValue = inkStyle[key];
87+
return typeof newValue === 'number' && typeof oldValue === 'number'
88+
? Math.round(newValue) === Math.round(oldValue)
89+
: newValue === oldValue;
90+
});
91+
if (!isEqual) {
92+
setInkStyle(newInkStyle);
93+
}
8094
});
8195

8296
return cleanInkBarRaf;
83-
}, [activeTabOffset, horizontal, rtl, align, getLength]);
97+
}, [JSON.stringify(activeTabOffset), horizontal, rtl, align, getLength]);
8498

8599
return { style: inkStyle };
86100
};

tests/index.test.tsx

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,20 @@ describe('Tabs.Basic', () => {
611611
rerender(getTabs({ indicator: { size: origin => origin - 2 } }));
612612
await waitFakeTimer();
613613
expect(container.querySelector('.rc-tabs-ink-bar')).toHaveStyle({ width: '18px' });
614+
615+
// 测试小数值的处理
616+
rerender(getTabs({ indicator: { size: origin => origin - 0.5 } }));
617+
await waitFakeTimer();
618+
expect(container.querySelector('.rc-tabs-ink-bar')).toHaveStyle({ width: '19.5px' });
619+
620+
// 测试防抖动功能
621+
rerender(getTabs({ indicator: { size: origin => origin - 0.1 } }));
622+
await waitFakeTimer();
623+
const initialWidth = (container.querySelector('.rc-tabs-ink-bar') as HTMLElement).style.width;
624+
625+
rerender(getTabs({ indicator: { size: origin => origin - 0.2 } }));
626+
await waitFakeTimer();
627+
expect(container.querySelector('.rc-tabs-ink-bar')).toHaveStyle({ width: initialWidth });
614628
});
615629

616630
it('Add span to text label when have icon', () => {
@@ -624,7 +638,7 @@ describe('Tabs.Basic', () => {
624638
});
625639

626640
it('support indicatorAlign', async () => {
627-
const { container: startContainer } = render(
641+
const { container: startContainer, rerender: rerenderStart } = render(
628642
<Tabs
629643
items={[{ key: 'test', label: 'test', icon: 'test' }]}
630644
indicator={{ size: origin => origin - 20, align: 'start' }}
@@ -653,10 +667,21 @@ describe('Tabs.Basic', () => {
653667

654668
expect(parseInt(startBar.style.left)).toBeLessThanOrEqual(parseInt(centerBar.style.left));
655669
expect(parseInt(centerBar.style.left)).toBeLessThanOrEqual(parseInt(endBar.style.left));
670+
671+
// 测试动态切换 align
672+
rerenderStart(
673+
<Tabs
674+
items={[{ key: 'test', label: 'test', icon: 'test' }]}
675+
indicator={{ size: origin => origin - 20, align: 'center' }}
676+
/>,
677+
);
678+
await waitFakeTimer();
679+
const newStartBar = startContainer.querySelector<HTMLDivElement>(selectors);
680+
expect(parseInt(newStartBar.style.left)).toBe(parseInt(centerBar.style.left));
656681
});
657682

658683
it('support indicatorAlign when tabPosition=left', async () => {
659-
const { container: startContainer } = render(
684+
const { container: startContainer, rerender: rerenderStart } = render(
660685
<Tabs
661686
tabPosition="left"
662687
items={[{ key: 'test', label: 'test', icon: 'test' }]}
@@ -688,6 +713,31 @@ describe('Tabs.Basic', () => {
688713

689714
expect(parseInt(startBar.style.top)).toBeLessThanOrEqual(parseInt(centerBar.style.top));
690715
expect(parseInt(centerBar.style.top)).toBeLessThanOrEqual(parseInt(endBar.style.top));
716+
717+
// 测试动态切换 align
718+
rerenderStart(
719+
<Tabs
720+
tabPosition="left"
721+
items={[{ key: 'test', label: 'test', icon: 'test' }]}
722+
indicator={{ size: origin => origin - 20, align: 'center' }}
723+
/>,
724+
);
725+
await waitFakeTimer();
726+
const newStartBar = startContainer.querySelector<HTMLDivElement>(selectors);
727+
expect(parseInt(newStartBar.style.top)).toBe(parseInt(centerBar.style.top));
728+
729+
// 测试 RTL 模式下的位置
730+
const { container: rtlContainer } = render(
731+
<Tabs
732+
tabPosition="left"
733+
direction="rtl"
734+
items={[{ key: 'test', label: 'test', icon: 'test' }]}
735+
indicator={{ size: origin => origin - 20, align: 'center' }}
736+
/>,
737+
);
738+
await waitFakeTimer();
739+
const rtlBar = rtlContainer.querySelector<HTMLDivElement>(selectors);
740+
expect(rtlBar.style.transform).toContain('translateY(-50%)');
691741
});
692742
it('support classnames and styles', () => {
693743
const customClassNames = {

vercel.json

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)