diff --git a/examples/index.tsx b/examples/index.tsx
index 25d21ce..6231e7d 100644
--- a/examples/index.tsx
+++ b/examples/index.tsx
@@ -9,7 +9,6 @@ import './styles.css';
const Placeholder = () =>
;
const Test = () => {
const div = React.useRef(null);
- const rect = useRect(div);
return ;
};
@@ -23,6 +22,7 @@ class Example extends React.PureComponent<
private container4: React.RefObject;
private container5: React.RefObject;
private container6: React.RefObject;
+ private container7: React.RefObject;
constructor(props) {
super(props);
@@ -32,6 +32,7 @@ class Example extends React.PureComponent<
this.container4 = React.createRef();
this.container5 = React.createRef();
this.container6 = React.createRef();
+ this.container7 = React.createRef();
this.state = {
disableHeader: false,
disableAll: false,
@@ -178,6 +179,14 @@ class Example extends React.PureComponent<
+
+
disableHardwareAcceleration: true
@@ -190,6 +199,21 @@ class Example extends React.PureComponent<
}
}
+const DynamicContent = () => {
+ const [count, setCount] = React.useState(1);
+
+ return (
+
+
+ {Array(count)
+ .fill(null)
+ .map((_, index) => (
+
{index}
+ ))}
+
+ );
+};
+
render(
diff --git a/lib/ElementResizeObserver.tsx b/lib/ElementResizeObserver.tsx
new file mode 100644
index 0000000..7424eee
--- /dev/null
+++ b/lib/ElementResizeObserver.tsx
@@ -0,0 +1,58 @@
+import * as React from 'react';
+import { IRect } from 'react-viewport-utils';
+
+interface IElementResizeObserverProps {
+ stickyRef: React.RefObject;
+ onUpdate: (rect: IRect) => void;
+}
+
+class ElementResizeObserver extends React.PureComponent<
+ IElementResizeObserverProps
+> {
+ private resizeObserver: ResizeObserver | null;
+ constructor(props: IElementResizeObserverProps) {
+ super(props);
+ this.resizeObserver = null;
+ }
+
+ componentDidMount() {
+ this.resetObserver();
+ this.installObserver();
+ }
+
+ componentDidUpdate() {
+ this.resetObserver();
+ this.installObserver();
+ }
+
+ componentWillUnmount() {
+ this.resetObserver();
+ }
+
+ installObserver() {
+ if (!this.props.stickyRef.current) {
+ return;
+ }
+ if (typeof window.ResizeObserver !== 'undefined') {
+ this.resizeObserver = new window.ResizeObserver(entries => {
+ if (entries && entries[0] && entries[0].contentRect) {
+ this.props.onUpdate(entries[0].contentRect);
+ }
+ });
+ this.resizeObserver!.observe(this.props.stickyRef.current);
+ }
+ }
+
+ resetObserver() {
+ if (this.resizeObserver) {
+ this.resizeObserver.disconnect();
+ this.resizeObserver = null;
+ }
+ }
+
+ render(): null {
+ return null;
+ }
+}
+
+export default ElementResizeObserver;
diff --git a/lib/StickyPlaceholder.tsx b/lib/StickyPlaceholder.tsx
index 73b49d1..6a8bc87 100644
--- a/lib/StickyPlaceholder.tsx
+++ b/lib/StickyPlaceholder.tsx
@@ -6,6 +6,7 @@ import {
requestAnimationFrame,
cancelAnimationFrame,
} from 'react-viewport-utils';
+import ElementResizeObserver from './ElementResizeObserver';
interface IProps {
disableResizing: boolean;
@@ -22,11 +23,12 @@ interface IState {
isWaitingForRecalculation: boolean;
stickyHeight: number | null;
stickyWidth: number | null;
- clientSize: string | null;
+ clientHash: string | null;
}
class StickyPlaceholder extends React.Component {
private recalculationTick?: number;
+ private lastDimensions?: IDimensions;
static defaultProps = {
style: {},
};
@@ -36,7 +38,7 @@ class StickyPlaceholder extends React.Component {
isWaitingForRecalculation: false,
stickyHeight: null,
stickyWidth: null,
- clientSize: null,
+ clientHash: null,
};
componentWillUnmount() {
@@ -58,16 +60,17 @@ class StickyPlaceholder extends React.Component {
{ dimensions }: { dimensions: IDimensions },
stickyRect: IRect | null,
) => {
+ this.lastDimensions = dimensions;
const { width, clientWidth } = dimensions;
- const nextClientSize = [width, clientWidth].join(',');
+ const nextClientHash = [width, clientWidth].join(',');
if (
!this.state.isWaitingForRecalculation &&
- this.state.clientSize !== nextClientSize
+ this.state.clientHash !== nextClientHash
) {
this.setState(
{
- clientSize: nextClientSize,
+ clientHash: nextClientHash,
isRecalculating: true,
isWaitingForRecalculation: true,
},
@@ -83,14 +86,29 @@ class StickyPlaceholder extends React.Component {
return;
}
- if (stickyRect && this.state.isWaitingForRecalculation) {
- this.setState({
- clientSize: nextClientSize,
- stickyHeight: stickyRect.height,
- stickyWidth: stickyRect.width,
- isWaitingForRecalculation: false,
- });
- return;
+ if (stickyRect) {
+ if (
+ this.state.isWaitingForRecalculation ||
+ stickyRect.height !== this.state.stickyHeight ||
+ stickyRect.width !== this.state.stickyWidth
+ ) {
+ this.setState({
+ clientHash: nextClientHash,
+ stickyHeight: stickyRect.height,
+ stickyWidth: stickyRect.width,
+ isWaitingForRecalculation: false,
+ });
+ return;
+ }
+ }
+ };
+
+ handleElementResize = (stickyRect: IRect) => {
+ if (this.lastDimensions) {
+ this.handleDimensionsUpdate(
+ { dimensions: this.lastDimensions },
+ stickyRect,
+ );
}
};
@@ -119,13 +137,19 @@ class StickyPlaceholder extends React.Component {
})}
{!this.props.disableResizing && (
-
+ <>
+
+
+ >
)}
>
);
diff --git a/lib/globals.d.ts b/lib/globals.d.ts
new file mode 100644
index 0000000..3458738
--- /dev/null
+++ b/lib/globals.d.ts
@@ -0,0 +1,67 @@
+// @see https://gist.github.com/strothj/708afcf4f01dd04de8f49c92e88093c3
+interface Window {
+ ResizeObserver: ResizeObserver;
+}
+
+/**
+ * The ResizeObserver interface is used to observe changes to Element's content
+ * rect.
+ *
+ * It is modeled after MutationObserver and IntersectionObserver.
+ */
+interface ResizeObserver {
+ new (callback: ResizeObserverCallback): ResizeObserver;
+
+ /**
+ * Adds target to the list of observed elements.
+ */
+ observe: (target: Element) => void;
+
+ /**
+ * Removes target from the list of observed elements.
+ */
+ unobserve: (target: Element) => void;
+
+ /**
+ * Clears both the observationTargets and activeTargets lists.
+ */
+ disconnect: () => void;
+}
+
+/**
+ * This callback delivers ResizeObserver's notifications. It is invoked by a
+ * broadcast active observations algorithm.
+ */
+interface ResizeObserverCallback {
+ (entries: ResizeObserverEntry[], observer: ResizeObserver): void;
+}
+
+interface ResizeObserverEntry {
+ /**
+ * @param target The Element whose size has changed.
+ */
+ new (target: Element): ResizeObserverEntry;
+
+ /**
+ * The Element whose size has changed.
+ */
+ readonly target: Element;
+
+ /**
+ * Element's content rect when ResizeObserverCallback is invoked.
+ */
+ readonly contentRect: DOMRectReadOnly;
+}
+
+interface DOMRectReadOnly {
+ readonly x: number;
+ readonly y: number;
+ readonly width: number;
+ readonly height: number;
+ readonly top: number;
+ readonly right: number;
+ readonly bottom: number;
+ readonly left: number;
+
+ toJSON: () => any;
+}