+ {
+ divNode = node;
+ }}
+ />
+ ,
+ );
+
+ // First trigger
+ wrapper.instance().onResize([{ target: divNode }]);
+ onResize.mockReset();
+
+ // Repeat trigger should not trigger outer `onResize` with shaking
+ for (let i = 0; i < 10; i += 1) {
+ wrapper.instance().onResize([{ target: divNode }]);
+ }
+
+ expect(onResize).not.toHaveBeenCalled();
+ });
+ });
});
diff --git a/components/_util/resizeObserver.tsx b/components/_util/resizeObserver.tsx
index 3fd815c7cd..8efec93e18 100644
--- a/components/_util/resizeObserver.tsx
+++ b/components/_util/resizeObserver.tsx
@@ -10,9 +10,19 @@ interface ResizeObserverProps {
onResize?: () => void;
}
-class ReactResizeObserver extends React.Component {
+interface ResizeObserverState {
+ height: number;
+ width: number;
+}
+
+class ReactResizeObserver extends React.Component {
resizeObserver: ResizeObserver | null = null;
+ state = {
+ width: 0,
+ height: 0,
+ };
+
componentDidMount() {
this.onComponentUpdated();
}
@@ -38,10 +48,30 @@ class ReactResizeObserver extends React.Component {
}
}
- onResize = () => {
+ onResize: ResizeObserverCallback = (entries: ResizeObserverEntry[]) => {
const { onResize } = this.props;
- if (onResize) {
- onResize();
+
+ const { target } = entries[0];
+
+ const { width, height } = target.getBoundingClientRect();
+
+ /**
+ * Resize observer trigger when content size changed.
+ * In most case we just care about element size,
+ * let's use `boundary` instead of `contentRect` here to avoid shaking.
+ */
+ const fixedWidth = Math.floor(width);
+ const fixedHeight = Math.floor(height);
+
+ if (this.state.width !== fixedWidth || this.state.height !== fixedHeight) {
+ this.setState({
+ width: fixedWidth,
+ height: fixedHeight,
+ });
+
+ if (onResize) {
+ onResize();
+ }
}
};
diff --git a/components/affix/__tests__/Affix.test.js b/components/affix/__tests__/Affix.test.js
index f5051dcfb3..f9531e81b8 100644
--- a/components/affix/__tests__/Affix.test.js
+++ b/components/affix/__tests__/Affix.test.js
@@ -3,6 +3,7 @@ import { mount } from 'enzyme';
import Affix from '..';
import { getObserverEntities } from '../utils';
import Button from '../../button';
+import { spyElementPrototype } from '../../__tests__/util/domHook';
const events = {};
@@ -40,6 +41,7 @@ class AffixMounter extends React.Component {
describe('Affix Render', () => {
let wrapper;
+ let domMock;
const classRect = {
container: {
@@ -48,23 +50,21 @@ describe('Affix Render', () => {
},
};
- const originGetBoundingClientRect = HTMLElement.prototype.getBoundingClientRect;
- HTMLElement.prototype.getBoundingClientRect = function getBoundingClientRect() {
- return (
- classRect[this.className] || {
- top: 0,
- bottom: 0,
- }
- );
- };
-
beforeAll(() => {
jest.useFakeTimers();
+ domMock = spyElementPrototype(HTMLElement, 'getBoundingClientRect', function mockBounding() {
+ return (
+ classRect[this.className] || {
+ top: 0,
+ bottom: 0,
+ }
+ );
+ });
});
afterAll(() => {
jest.useRealTimers();
- HTMLElement.prototype.getBoundingClientRect = originGetBoundingClientRect;
+ domMock.mockRestore();
});
const movePlaceholder = top => {
classRect.fixed = {
@@ -185,7 +185,7 @@ describe('Affix Render', () => {
.find('ReactResizeObserver')
.at(index)
.instance()
- .onResize();
+ .onResize([{ target: { getBoundingClientRect: () => ({ width: 99, height: 99 }) } }]);
jest.runAllTimers();
expect(updateCalled).toHaveBeenCalled();