diff --git a/packages/react-intersection-observer/src/InView.tsx b/packages/react-intersection-observer/src/InView.tsx
index d130060a..ab159dc5 100644
--- a/packages/react-intersection-observer/src/InView.tsx
+++ b/packages/react-intersection-observer/src/InView.tsx
@@ -91,6 +91,7 @@ export class InView extends React.Component<
prevProps.scrollMargin !== this.props.scrollMargin ||
prevProps.root !== this.props.root ||
prevProps.threshold !== this.props.threshold ||
+ prevProps.triggerOnce !== this.props.triggerOnce ||
prevProps.skip !== this.props.skip ||
prevProps.trackVisibility !== this.props.trackVisibility ||
prevProps.delay !== this.props.delay
diff --git a/packages/react-intersection-observer/src/__tests__/InView.test.tsx b/packages/react-intersection-observer/src/__tests__/InView.test.tsx
index 213a51cf..971e9d0c 100644
--- a/packages/react-intersection-observer/src/__tests__/InView.test.tsx
+++ b/packages/react-intersection-observer/src/__tests__/InView.test.tsx
@@ -144,6 +144,105 @@ test("Should unobserve when triggerOnce comes into view", () => {
expect(instance.unobserve).toHaveBeenCalled();
});
+test("Should resume observing when triggerOnce is disabled while in view", () => {
+ const callback = vi.fn();
+ const { container, rerender } = render(
+
+ Inner
+ ,
+ );
+ const element = container.children[0];
+ const initialObserver = intersectionMockInstance(element);
+ vi.spyOn(initialObserver, "unobserve");
+
+ mockAllIsIntersecting(true);
+ expect(callback).toHaveBeenCalledTimes(1);
+ expect(callback).toHaveBeenNthCalledWith(
+ 1,
+ true,
+ expect.objectContaining({ isIntersecting: true }),
+ );
+ expect(initialObserver.unobserve).toHaveBeenCalledWith(element);
+
+ callback.mockClear();
+ rerender(
+
+ Inner
+ ,
+ );
+ const resumedObserver = intersectionMockInstance(element);
+ expect(resumedObserver).not.toBe(initialObserver);
+
+ // A fresh observer reports the element's current state.
+ mockAllIsIntersecting(true);
+ expect(callback).toHaveBeenNthCalledWith(
+ 1,
+ true,
+ expect.objectContaining({ isIntersecting: true }),
+ );
+
+ mockAllIsIntersecting(false);
+ expect(callback).toHaveBeenNthCalledWith(
+ 2,
+ false,
+ expect.objectContaining({ isIntersecting: false }),
+ );
+
+ mockAllIsIntersecting(true);
+ expect(callback).toHaveBeenNthCalledWith(
+ 3,
+ true,
+ expect.objectContaining({ isIntersecting: true }),
+ );
+ expect(callback).toHaveBeenCalledTimes(3);
+});
+
+test("Should stop observing when triggerOnce is enabled while in view", () => {
+ const callback = vi.fn();
+ const { container, rerender } = render(
+
+ Inner
+ ,
+ );
+ const element = container.children[0];
+ const initialObserver = intersectionMockInstance(element);
+ vi.spyOn(initialObserver, "unobserve");
+
+ mockAllIsIntersecting(true);
+ expect(callback).toHaveBeenCalledTimes(1);
+ expect(callback).toHaveBeenNthCalledWith(
+ 1,
+ true,
+ expect.objectContaining({ isIntersecting: true }),
+ );
+
+ callback.mockClear();
+ rerender(
+
+ Inner
+ ,
+ );
+ expect(initialObserver.unobserve).toHaveBeenCalledWith(element);
+
+ const triggerOnceObserver = intersectionMockInstance(element);
+ expect(triggerOnceObserver).not.toBe(initialObserver);
+ vi.spyOn(triggerOnceObserver, "unobserve");
+
+ // A fresh observer reports the element's current state before triggerOnce stops it.
+ mockAllIsIntersecting(true);
+ expect(callback).toHaveBeenCalledTimes(1);
+ expect(callback).toHaveBeenNthCalledWith(
+ 1,
+ true,
+ expect.objectContaining({ isIntersecting: true }),
+ );
+ expect(triggerOnceObserver.unobserve).toHaveBeenCalledWith(element);
+
+ mockAllIsIntersecting(false);
+ mockAllIsIntersecting(true);
+ expect(callback).toHaveBeenCalledTimes(1);
+});
+
test("Should unobserve when unmounted", () => {
const { container, unmount } = render(Inner);
const instance = intersectionMockInstance(container.children[0]);