Skip to content

Commit

Permalink
fix: Debounce of onOpenChange repeat trigger
Browse files Browse the repository at this point in the history
  • Loading branch information
zombieJ committed Nov 30, 2019
1 parent c69f892 commit 1d1d86e
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 10 deletions.
11 changes: 10 additions & 1 deletion examples/uncontrolled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,23 @@ export default () => (
<div>
<div style={{ margin: '0 8px' }}>
<h3>Uncontrolled</h3>
<Picker<Moment>
generateConfig={momentGenerateConfig}
locale={zhCN}
picker="week"
allowClear
onOpenChange={open => {
console.log('1 =>', open);
}}
/>
<Picker<Moment>
generateConfig={momentGenerateConfig}
locale={zhCN}
picker="week"
allowClear
open
onOpenChange={open => {
console.log('=>', open);
console.log('2 =>', open);
}}
/>
<button type="button">233</button>
Expand Down
29 changes: 25 additions & 4 deletions src/Picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { PickerMode } from './interface';
import {
getDefaultFormat,
getInputSize,
addGlobalClickEvent,
addGlobalMouseDownEvent,
} from './utils/uiUtil';

export interface PickerSharedProps<DateType> {
Expand Down Expand Up @@ -326,10 +326,25 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
}
};

const onInputBlur: React.FocusEventHandler<HTMLInputElement> = e => {
/**
* We will prevent blur to handle open event when user click outside,
* since this will repeat trigger `onOpenChange` event.
*/
const preventBlurRef = React.useRef<boolean>(false);

const triggerClose = () => {
triggerOpen(false);
setInnerValue(selectedValue);
triggerChange(selectedValue);
};

const onInputBlur: React.FocusEventHandler<HTMLInputElement> = e => {
if (preventBlurRef.current) {
preventBlurRef.current = false;
return;
}

triggerClose();
setFocused(false);

if (onBlur) {
Expand Down Expand Up @@ -361,7 +376,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {

// Global click handler
React.useEffect(() =>
addGlobalClickEvent(({ target }: MouseEvent) => {
addGlobalMouseDownEvent(({ target }: MouseEvent) => {
if (
mergedOpen &&
panelDivRef.current &&
Expand All @@ -370,7 +385,13 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
!inputDivRef.current.contains(target as Node) &&
onOpenChange
) {
onOpenChange(false);
preventBlurRef.current = true;
triggerClose();

// Always set back in case `onBlur` prevented by user
window.setTimeout(() => {
preventBlurRef.current = false;
}, 0);
}
}),
);
Expand Down
6 changes: 3 additions & 3 deletions src/utils/uiUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ type ClickEventHandler = (event: MouseEvent) => void;
let globalClickFunc: ClickEventHandler | null = null;
const clickCallbacks = new Set<ClickEventHandler>();

export function addGlobalClickEvent(callback: ClickEventHandler) {
export function addGlobalMouseDownEvent(callback: ClickEventHandler) {
if (
!globalClickFunc &&
typeof window !== 'undefined' &&
Expand All @@ -168,15 +168,15 @@ export function addGlobalClickEvent(callback: ClickEventHandler) {
queueFunc(e);
});
};
window.addEventListener('click', globalClickFunc);
window.addEventListener('mousedown', globalClickFunc);
}

clickCallbacks.add(callback);

return () => {
clickCallbacks.delete(callback);
if (clickCallbacks.size === 0) {
window.removeEventListener('click', globalClickFunc!);
window.removeEventListener('mousedown', globalClickFunc!);
globalClickFunc = null;
}
};
Expand Down
8 changes: 6 additions & 2 deletions tests/picker.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,17 +120,21 @@ describe('Basic', () => {
});

it('fixed open need repeat trigger onOpenChange', () => {
jest.useFakeTimers();
const onOpenChange = jest.fn();
mount(<MomentPicker onOpenChange={onOpenChange} open />);
const wrapper = mount(<MomentPicker onOpenChange={onOpenChange} open />);

for (let i = 0; i < 10; i += 1) {
const clickEvent = new Event('click');
const clickEvent = new Event('mousedown');
Object.defineProperty(clickEvent, 'target', {
get: () => document.body,
});
window.dispatchEvent(clickEvent);
wrapper.find('input').simulate('blur');
expect(onOpenChange).toHaveBeenCalledTimes(i + 1);
}
jest.runAllTimers();
jest.useRealTimers();
});

it('disabled should not open', () => {
Expand Down

1 comment on commit 1d1d86e

@vercel
Copy link

@vercel vercel bot commented on 1d1d86e Nov 30, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.