Browse Source

refactor: remove rc-util/lib/Dom/addEventListener (#42464)

* docs: fix typo in docs

* remove rc-util/lib/Dom/addEventListener

* fix

* fix
pull/42481/head
lijianan 2 years ago
committed by GitHub
parent
commit
8f1d496972
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 28
      components/affix/__tests__/Affix.test.tsx
  2. 45
      components/affix/index.tsx
  3. 80
      components/affix/utils.ts

28
components/affix/__tests__/Affix.test.tsx

@ -1,12 +1,11 @@
import React, { useEffect, useRef } from 'react';
import type { CSSProperties } from 'react';
import React, { useEffect, useRef } from 'react';
import type { InternalAffixClass } from '..';
import Affix from '..';
import accessibilityTest from '../../../tests/shared/accessibilityTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { render, triggerResize, waitFakeTimer } from '../../../tests/utils';
import Button from '../../button';
import { addObserveTarget, getObserverEntities } from '../utils';
const events: Partial<Record<keyof HTMLElementEventMap, (ev: Partial<Event>) => void>> = {};
@ -49,8 +48,6 @@ describe('Affix Render', () => {
beforeEach(() => {
jest.useFakeTimers();
const entities = getObserverEntities();
entities.splice(0, entities.length);
});
beforeAll(() => {
@ -157,23 +154,6 @@ describe('Affix Render', () => {
expect(affixInstance!.state.placeholderStyle).toBe(undefined);
});
it('instance change', async () => {
const container = document.createElement('div');
document.body.appendChild(container);
let target: HTMLDivElement | null = container;
const getTarget = () => target;
const { rerender } = render(<Affix target={getTarget}>{null}</Affix>);
await waitFakeTimer();
expect(getObserverEntities()).toHaveLength(1);
expect(getObserverEntities()[0].target).toBe(container);
target = null;
rerender(<Affix>{null}</Affix>);
expect(getObserverEntities()).toHaveLength(1);
expect(getObserverEntities()[0].target).toBe(window);
});
it('check position change before measure', async () => {
const { container } = render(
<>
@ -264,11 +244,5 @@ describe('Affix Render', () => {
expect(updateCalled).toHaveBeenCalled();
});
});
it('addObserveTarget should not Throw Error when target is null', () => {
expect(() => {
addObserveTarget(null);
}).not.toThrow();
});
});
});

45
components/affix/index.tsx

@ -2,18 +2,21 @@ import classNames from 'classnames';
import ResizeObserver from 'rc-resize-observer';
import omit from 'rc-util/lib/omit';
import React, { createRef, forwardRef, useContext } from 'react';
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame';
import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame';
import useStyle from './style';
import {
addObserveTarget,
getFixedBottom,
getFixedTop,
getTargetRect,
removeObserveTarget,
} from './utils';
import { getFixedBottom, getFixedTop, getTargetRect } from './utils';
const TRIGGER_EVENTS = [
'resize',
'scroll',
'touchstart',
'touchmove',
'touchend',
'pageshow',
'load',
] as const;
function getDefaultTarget() {
return typeof window !== 'undefined' ? window : null;
@ -87,8 +90,11 @@ class InternalAffix extends React.Component<InternalAffixProps, AffixState> {
if (targetFunc) {
// [Legacy] Wait for parent component ref has its value.
// We should use target as directly element instead of function which makes element check hard.
const target = targetFunc?.() || null;
this.timer = setTimeout(() => {
addObserveTarget(targetFunc(), this);
TRIGGER_EVENTS.forEach((eventName) => {
target?.addEventListener(eventName, this.lazyUpdatePosition);
});
// Mock Event object.
this.updatePosition();
});
@ -99,11 +105,12 @@ class InternalAffix extends React.Component<InternalAffixProps, AffixState> {
const { prevTarget } = this.state;
const targetFunc = this.getTargetFunc();
const newTarget = targetFunc?.() || null;
if (prevTarget !== newTarget) {
removeObserveTarget(this);
if (newTarget) {
addObserveTarget(newTarget, this);
TRIGGER_EVENTS.forEach((eventName) => {
prevTarget?.removeEventListener(eventName, this.lazyUpdatePosition);
newTarget?.addEventListener(eventName, this.lazyUpdatePosition);
});
// Mock Event object.
this.updatePosition();
}
@ -126,7 +133,17 @@ class InternalAffix extends React.Component<InternalAffixProps, AffixState> {
clearTimeout(this.timer);
this.timer = null;
}
removeObserveTarget(this);
const { prevTarget } = this.state;
const targetFunc = this.getTargetFunc();
const newTarget = targetFunc?.();
TRIGGER_EVENTS.forEach((eventName) => {
newTarget?.removeEventListener(eventName, this.lazyUpdatePosition);
});
if (prevTarget !== newTarget) {
TRIGGER_EVENTS.forEach((eventName) => {
prevTarget?.removeEventListener(eventName, this.lazyUpdatePosition);
});
}
this.updatePosition.cancel();
// https://github.com/ant-design/ant-design/issues/22683
this.lazyUpdatePosition.cancel();

80
components/affix/utils.ts

@ -1,6 +1,3 @@
import addEventListener from 'rc-util/lib/Dom/addEventListener';
import type { InternalAffixClass } from '.';
export type BindElement = HTMLElement | Window | null | undefined;
export function getTargetRect(target: BindElement): DOMRect {
@ -27,80 +24,3 @@ export function getFixedBottom(
}
return undefined;
}
// ======================== Observer ========================
const TRIGGER_EVENTS = [
'resize',
'scroll',
'touchstart',
'touchmove',
'touchend',
'pageshow',
'load',
];
interface ObserverEntity {
target: HTMLElement | Window;
affixList: any[];
eventHandlers: { [eventName: string]: any };
}
let observerEntities: ObserverEntity[] = [];
export function getObserverEntities() {
// Only used in test env. Can be removed if refactor.
return observerEntities;
}
export function addObserveTarget<T extends InternalAffixClass>(
target: HTMLElement | Window | null,
affix?: T,
): void {
if (!target) {
return;
}
let entity = observerEntities.find((item) => item.target === target);
if (entity) {
entity.affixList.push(affix);
} else {
entity = {
target,
affixList: [affix],
eventHandlers: {},
};
observerEntities.push(entity);
// Add listener
TRIGGER_EVENTS.forEach((eventName) => {
entity!.eventHandlers[eventName] = addEventListener(target, eventName, () => {
entity!.affixList.forEach((targetAffix) => {
targetAffix.lazyUpdatePosition();
});
});
});
}
}
export function removeObserveTarget<T extends InternalAffixClass>(affix: T): void {
const observerEntity = observerEntities.find((oriObserverEntity) => {
const hasAffix = oriObserverEntity.affixList.some((item) => item === affix);
if (hasAffix) {
oriObserverEntity.affixList = oriObserverEntity.affixList.filter((item) => item !== affix);
}
return hasAffix;
});
if (observerEntity && observerEntity.affixList.length === 0) {
observerEntities = observerEntities.filter((item) => item !== observerEntity);
// Remove listener
TRIGGER_EVENTS.forEach((eventName) => {
const handler = observerEntity.eventHandlers[eventName];
if (handler && handler.remove) {
handler.remove();
}
});
}
}

Loading…
Cancel
Save