From c0afab51b48b61fd2d0d3937f367bdd37809d822 Mon Sep 17 00:00:00 2001 From: Kermit Date: Mon, 7 Sep 2020 21:10:54 +0800 Subject: [PATCH 1/3] chore(useHoverValue): add raf to optimize performance --- src/hooks/useHoverValue.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/hooks/useHoverValue.ts b/src/hooks/useHoverValue.ts index b4135f9a0..a0530c6cd 100644 --- a/src/hooks/useHoverValue.ts +++ b/src/hooks/useHoverValue.ts @@ -1,11 +1,23 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef } from 'react'; import useValueTexts, { ValueTextConfig } from './useValueTexts'; export default function useHoverValue( valueText: string, { formatList, generateConfig, locale }: ValueTextConfig, ): [string, (date: DateType) => void, (date: DateType) => void] { - const [value, setValue] = useState(null); + const [value, internalSetValue] = useState(null); + const raf = useRef(null); + + function setValue(val: DateType, immediately: boolean = false) { + cancelAnimationFrame(raf.current); + if (immediately) { + internalSetValue(val); + return; + } + raf.current = requestAnimationFrame(() => { + internalSetValue(val); + }); + } const [, firstText] = useValueTexts(value, { formatList, @@ -25,5 +37,7 @@ export default function useHoverValue( onLeave(); }, [valueText]); + useEffect(() => () => cancelAnimationFrame(raf.current), []); + return [firstText, onEnter, onLeave]; } From 47eb16ca7404ed7d012e96c6629f66558107a4c1 Mon Sep 17 00:00:00 2001 From: Kermit Date: Mon, 7 Sep 2020 21:32:46 +0800 Subject: [PATCH 2/3] optimize(hoverValue): add raf to optimize performance --- src/RangePicker.tsx | 8 ++--- src/hooks/useHoverValue.ts | 8 ++--- tests/picker.spec.tsx | 14 ++++++++ tests/range.spec.tsx | 67 ++++++++++++++++++++++++-------------- 4 files changed, 65 insertions(+), 32 deletions(-) diff --git a/src/RangePicker.tsx b/src/RangePicker.tsx index d994761ce..d2ea4e283 100644 --- a/src/RangePicker.tsx +++ b/src/RangePicker.tsx @@ -564,9 +564,9 @@ function InnerRangePicker(props: RangePickerProps) { const onDateMouseLeave = () => { setHoverRangedValue(updateValues(selectedValue, null, mergedActivePickerIndex)); if (mergedActivePickerIndex === 0) { - onStartLeave(null); + onStartLeave(); } else { - onEndLeave(null); + onEndLeave(); } }; @@ -1013,9 +1013,9 @@ function InnerRangePicker(props: RangePickerProps) { triggerChange(values, mergedActivePickerIndex); // clear hover value style if (mergedActivePickerIndex === 0) { - onStartLeave(null); + onStartLeave(true); } else { - onEndLeave(null); + onEndLeave(true); } } else { setSelectedValue(values); diff --git a/src/hooks/useHoverValue.ts b/src/hooks/useHoverValue.ts index a0530c6cd..0ff4f389b 100644 --- a/src/hooks/useHoverValue.ts +++ b/src/hooks/useHoverValue.ts @@ -4,7 +4,7 @@ import useValueTexts, { ValueTextConfig } from './useValueTexts'; export default function useHoverValue( valueText: string, { formatList, generateConfig, locale }: ValueTextConfig, -): [string, (date: DateType) => void, (date: DateType) => void] { +): [string, (date: DateType) => void, (immediately?: boolean) => void] { const [value, internalSetValue] = useState(null); const raf = useRef(null); @@ -29,12 +29,12 @@ export default function useHoverValue( setValue(date); } - function onLeave() { - setValue(null); + function onLeave(immediately: boolean = false) { + setValue(null, immediately); } useEffect(() => { - onLeave(); + onLeave(true); }, [valueText]); useEffect(() => () => cancelAnimationFrame(raf.current), []); diff --git a/tests/picker.spec.tsx b/tests/picker.spec.tsx index 1430fae3b..478fbcf82 100644 --- a/tests/picker.spec.tsx +++ b/tests/picker.spec.tsx @@ -740,14 +740,24 @@ describe('Picker.Basic', () => { }); describe('hover value', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + afterEach(() => { + jest.useRealTimers(); + }); it('should restore when leave', () => { const wrapper = mount(); const cell = wrapper.findCell(24); cell.simulate('mouseEnter'); + jest.runAllTimers(); + wrapper.update(); expect(wrapper.find('input').prop('value')).toBe('2020-07-24'); expect(wrapper.find('.rc-picker-input').hasClass('rc-picker-input-placeholder')).toBeTruthy(); cell.simulate('mouseLeave'); + jest.runAllTimers(); + wrapper.update(); expect(wrapper.find('input').prop('value')).toBe('2020-07-22'); expect(wrapper.find('.rc-picker-input').hasClass('rc-picker-input-placeholder')).toBeFalsy(); }); @@ -757,6 +767,8 @@ describe('Picker.Basic', () => { wrapper.openPicker(); const cell = wrapper.findCell(24); cell.simulate('mouseEnter'); + jest.runAllTimers(); + wrapper.update(); expect(wrapper.find('input').prop('value')).toBe('2020-07-24'); expect(wrapper.find('.rc-picker-input').hasClass('rc-picker-input-placeholder')).toBeTruthy(); @@ -770,6 +782,8 @@ describe('Picker.Basic', () => { wrapper.openPicker(); const cell = wrapper.findCell(24); cell.simulate('mouseEnter'); + jest.runAllTimers(); + wrapper.update(); expect(wrapper.find('input').prop('value')).toBe('2020-07-24'); expect(wrapper.find('.rc-picker-input').hasClass('rc-picker-input-placeholder')).toBeTruthy(); diff --git a/tests/range.spec.tsx b/tests/range.spec.tsx index c39c38ace..fbefa45a2 100644 --- a/tests/range.spec.tsx +++ b/tests/range.spec.tsx @@ -1430,6 +1430,13 @@ describe('Picker.Range', () => { }); describe('hover placeholder', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + afterEach(() => { + jest.useRealTimers(); + }); + const defaultValue: [Moment, Moment] = [getMoment('2020-07-22'), getMoment('2020-08-22')]; it('should restore when leave', () => { @@ -1439,6 +1446,8 @@ describe('Picker.Range', () => { wrapper.openPicker(0); const leftCell = wrapper.findCell(24); leftCell.simulate('mouseEnter'); + jest.runAllTimers(); + wrapper.update(); expect( wrapper .find('input') @@ -1465,6 +1474,8 @@ describe('Picker.Range', () => { ).toBeFalsy(); leftCell.simulate('mouseLeave'); + jest.runAllTimers(); + wrapper.update(); expect( wrapper .find('input') @@ -1496,6 +1507,8 @@ describe('Picker.Range', () => { wrapper.openPicker(1); const rightCell = wrapper.findCell(24, 1); rightCell.simulate('mouseEnter'); + jest.runAllTimers(); + wrapper.update(); expect( wrapper .find('input') @@ -1522,6 +1535,8 @@ describe('Picker.Range', () => { ).toBeTruthy(); rightCell.simulate('mouseLeave'); + jest.runAllTimers(); + wrapper.update(); expect( wrapper .find('input') @@ -1556,6 +1571,8 @@ describe('Picker.Range', () => { wrapper.openPicker(0); const leftCell = wrapper.findCell(24, 0); leftCell.simulate('mouseEnter'); + jest.runAllTimers(); + wrapper.update(); expect( wrapper .find('input') @@ -1610,6 +1627,8 @@ describe('Picker.Range', () => { // right const rightCell = wrapper.findCell(24, 1); rightCell.simulate('mouseEnter'); + jest.runAllTimers(); + wrapper.update(); expect( wrapper .find('input') @@ -1661,6 +1680,30 @@ describe('Picker.Range', () => { .hasClass('rc-picker-input-placeholder'), ).toBeFalsy(); }); + + // https://github.com/ant-design/ant-design/issues/26544 + it('should clean hover style when selecting the same value with last value', () => { + const wrapper = mount( + , + ); + + wrapper.openPicker(); + + wrapper.selectCell(24, 0); + expect( + wrapper + .find('input') + .first() + .prop('value'), + ).toBe('2020-07-24'); + expect( + wrapper + .find('input') + .first() + .hasClass('rc-picker-input-placeholder'), + ).toBeFalsy(); + expect(wrapper.isOpen()).toBeTruthy(); + }); }); // https://github.com/ant-design/ant-design/issues/25746 @@ -1699,30 +1742,6 @@ describe('Picker.Range', () => { expect(wrapper.find('.rc-picker-ok button').props().disabled).toBeTruthy(); }); - // https://github.com/ant-design/ant-design/issues/26544 - it('should clean hover style when selecting the same value with last value', () => { - const wrapper = mount( - , - ); - - wrapper.openPicker(); - - wrapper.selectCell(24, 0); - expect( - wrapper - .find('input') - .first() - .prop('value'), - ).toBe('2020-07-24'); - expect( - wrapper - .find('input') - .first() - .hasClass('rc-picker-input-placeholder'), - ).toBeFalsy(); - expect(wrapper.isOpen()).toBeTruthy(); - }); - // https://github.com/ant-design/ant-design/issues/26024 it('panel should keep open when nextValue is empty', () => { const wrapper = mount(); From 30c2f26bfcc56815febc319be691d7f7080b59dd Mon Sep 17 00:00:00 2001 From: Kermit Date: Mon, 7 Sep 2020 22:22:29 +0800 Subject: [PATCH 3/3] fix: update --- src/RangePicker.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/RangePicker.tsx b/src/RangePicker.tsx index d2ea4e283..a8c9d3a43 100644 --- a/src/RangePicker.tsx +++ b/src/RangePicker.tsx @@ -1013,9 +1013,9 @@ function InnerRangePicker(props: RangePickerProps) { triggerChange(values, mergedActivePickerIndex); // clear hover value style if (mergedActivePickerIndex === 0) { - onStartLeave(true); + onStartLeave(); } else { - onEndLeave(true); + onEndLeave(); } } else { setSelectedValue(values);