diff --git a/src/__tests__/useInView.test.tsx b/src/__tests__/useInView.test.tsx
index cc664e86..aa47c4a9 100644
--- a/src/__tests__/useInView.test.tsx
+++ b/src/__tests__/useInView.test.tsx
@@ -1,4 +1,4 @@
-import { render, screen } from "@testing-library/react";
+import { act, render, screen } from "@testing-library/react";
import React, { useCallback } from "react";
import { defaultFallbackInView, type IntersectionOptions } from "../index";
import {
@@ -118,6 +118,38 @@ test("should respect trigger once", () => {
getByText("true");
});
+test("should respect the threshold before triggering once", () => {
+ const { getByTestId, getByText } = render(
+ ,
+ );
+ const wrapper = getByTestId("wrapper");
+ const instance = intersectionMockInstance(wrapper);
+ const callback = vi.mocked(window.IntersectionObserver).mock.calls[0][0];
+ const createEntry = (
+ intersectionRatio: number,
+ ): IntersectionObserverEntry => ({
+ boundingClientRect: wrapper.getBoundingClientRect(),
+ intersectionRatio,
+ intersectionRect: wrapper.getBoundingClientRect(),
+ isIntersecting: true,
+ rootBounds: null,
+ target: wrapper,
+ time: performance.now(),
+ });
+
+ act(() => callback([createEntry(0.25)], instance));
+
+ getByText("false");
+ expect(instance.unobserve).not.toHaveBeenCalled();
+
+ act(() => callback([createEntry(0.5)], instance));
+
+ getByText("true");
+ expect(instance.unobserve).toHaveBeenCalledTimes(1);
+});
+
test("should trigger onChange", () => {
const onChange = vi.fn();
render();
diff --git a/src/useInView.tsx b/src/useInView.tsx
index 791c5014..c39871e1 100644
--- a/src/useInView.tsx
+++ b/src/useInView.tsx
@@ -85,7 +85,7 @@ export function useInView({
});
if (callback.current) callback.current(inView, entry);
- if (entry.isIntersecting && triggerOnce && unobserve) {
+ if (inView && triggerOnce && unobserve) {
// If it should only trigger once, unobserve the element after it's inView
unobserve();
unobserve = undefined;