Skip to content

Commit 3691668

Browse files
committed
change ref to be created inside useVisibilitySensor hook
1 parent 5694504 commit 3691668

File tree

2 files changed

+109
-123
lines changed

2 files changed

+109
-123
lines changed

lib/use-visibility-sensor.js

Lines changed: 107 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -98,71 +98,66 @@ function checkIsVisible(
9898
return isVisible;
9999
}
100100

101-
export default function useVisibilitySensor(
102-
nodeRef,
103-
{
104-
active = true,
105-
onChange,
106-
partialVisibility = false,
107-
minTopValue = 0,
108-
scrollCheck = false,
109-
scrollDelay = 250,
110-
scrollThrottle = -1,
111-
resizeCheck = false,
112-
resizeDelay = 250,
113-
resizeThrottle = -1,
114-
intervalCheck = true,
115-
intervalDelay = 100,
116-
delayedCall = false,
117-
offset = {},
118-
containment = null
119-
}
120-
) {
101+
export default function useVisibilitySensor({
102+
active = true,
103+
onChange,
104+
partialVisibility = false,
105+
minTopValue = 0,
106+
scrollCheck = false,
107+
scrollDelay = 250,
108+
scrollThrottle = -1,
109+
resizeCheck = false,
110+
resizeDelay = 250,
111+
resizeThrottle = -1,
112+
intervalCheck = true,
113+
intervalDelay = 100,
114+
delayedCall = false,
115+
offset = {},
116+
containment = null
117+
}) {
118+
const nodeRef = useRef();
121119
const debounceCheckRef = useRef();
122120
const intervalRef = useRef();
123-
const [isVisible, setIsVisible] = useState(false);
121+
const [isVisible, setIsVisible] = useState(null);
124122
const [visibilityRect, setVisibilityRect] = useState({});
125123

126124
const getContainer = useCallback(() => containment || window, [containment]);
127125

128126
// Check if the element is within the visible viewport
129-
const visibilityCheck = useCallback(
130-
() => {
131-
const el = nodeRef && nodeRef.current;
132-
// if the component has rendered to null, dont update visibility
133-
if (!el) {
134-
return;
135-
}
127+
const visibilityCheck = useCallback(() => {
128+
const el = nodeRef && nodeRef.current;
129+
// if the component has rendered to null, dont update visibility
130+
if (!el) {
131+
return;
132+
}
136133

137-
const rect = normalizeRect(roundRectDown(el.getBoundingClientRect()));
138-
const containmentRect = getContainmentRect(containment, offset);
139-
const nextVisibilityRect = getVisibilityRect(rect, containmentRect);
140-
const nextIsVisible = checkIsVisible(
141-
rect,
142-
containmentRect,
143-
nextVisibilityRect,
144-
partialVisibility,
145-
minTopValue
146-
);
147-
148-
// notify the parent when the value changes
149-
if (isVisible !== nextIsVisible) {
150-
setIsVisible(nextIsVisible);
151-
setVisibilityRect(nextVisibilityRect);
152-
if (onChange) onChange(nextIsVisible);
153-
}
154-
},
155-
[
156-
isVisible,
157-
offset,
158-
containment,
134+
const rect = normalizeRect(roundRectDown(el.getBoundingClientRect()));
135+
const containmentRect = getContainmentRect(containment, offset);
136+
const nextVisibilityRect = getVisibilityRect(rect, containmentRect);
137+
const nextIsVisible = checkIsVisible(
138+
rect,
139+
containmentRect,
140+
nextVisibilityRect,
159141
partialVisibility,
160-
minTopValue,
161-
onChange,
162-
setIsVisible,
163-
setVisibilityRect
164-
]
165-
);
142+
minTopValue
143+
);
144+
145+
// notify the parent when the value changes
146+
if (isVisible !== nextIsVisible) {
147+
setIsVisible(nextIsVisible);
148+
setVisibilityRect(nextVisibilityRect);
149+
if (onChange) onChange(nextIsVisible);
150+
}
151+
}, [
152+
isVisible,
153+
offset,
154+
containment,
155+
partialVisibility,
156+
minTopValue,
157+
onChange,
158+
setIsVisible,
159+
setVisibilityRect
160+
]);
166161

167162
const addEventListener = useCallback(
168163
(target, event, delay, throttle) => {
@@ -209,77 +204,69 @@ export default function useVisibilitySensor(
209204
[visibilityCheck]
210205
);
211206

212-
useEffect(
213-
() => {
214-
function watch() {
215-
if (debounceCheckRef.current || intervalRef.current) {
216-
return;
217-
}
218-
219-
if (intervalCheck) {
220-
intervalRef.current = setInterval(visibilityCheck, intervalDelay);
221-
}
222-
223-
if (scrollCheck) {
224-
addEventListener(
225-
getContainer(),
226-
"scroll",
227-
scrollDelay,
228-
scrollThrottle
229-
);
230-
}
207+
useEffect(() => {
208+
function watch() {
209+
if (debounceCheckRef.current || intervalRef.current) {
210+
return;
211+
}
231212

232-
if (resizeCheck) {
233-
addEventListener(window, "resize", resizeDelay, resizeThrottle);
234-
}
213+
if (intervalCheck) {
214+
intervalRef.current = setInterval(visibilityCheck, intervalDelay);
215+
}
235216

236-
// if dont need delayed call, check on load ( before the first interval fires )
237-
!delayedCall && visibilityCheck();
217+
if (scrollCheck) {
218+
addEventListener(getContainer(), "scroll", scrollDelay, scrollThrottle);
238219
}
239220

240-
if (active) {
241-
watch();
221+
if (resizeCheck) {
222+
addEventListener(window, "resize", resizeDelay, resizeThrottle);
242223
}
243224

244-
// stop any listeners and intervals on props change and re-registers
245-
return () => {
246-
if (debounceCheckRef.current) {
247-
const debounceCheck = debounceCheckRef.current;
248-
// clean up event listeners and their debounce callers
249-
for (let debounceEvent in debounceCheck) {
250-
if (debounceCheck.hasOwnProperty(debounceEvent)) {
251-
const debounceInfo = debounceCheck[debounceEvent];
252-
253-
clearTimeout(debounceInfo.getLastTimeout());
254-
debounceInfo.target.removeEventListener(
255-
debounceEvent,
256-
debounceInfo.fn
257-
);
258-
259-
debounceCheck[debounceEvent] = null;
260-
}
261-
}
262-
}
263-
debounceCheckRef.current = null;
225+
// if dont need delayed call, check on load ( before the first interval fires )
226+
!delayedCall && visibilityCheck();
227+
}
228+
229+
if (active) {
230+
watch();
231+
}
264232

265-
if (intervalRef.current) {
266-
intervalRef.current = clearInterval(intervalRef.current);
233+
// stop any listeners and intervals on props change and re-registers
234+
return () => {
235+
if (debounceCheckRef.current) {
236+
const debounceCheck = debounceCheckRef.current;
237+
// clean up event listeners and their debounce callers
238+
for (let debounceEvent in debounceCheck) {
239+
if (debounceCheck.hasOwnProperty(debounceEvent)) {
240+
const debounceInfo = debounceCheck[debounceEvent];
241+
242+
clearTimeout(debounceInfo.getLastTimeout());
243+
debounceInfo.target.removeEventListener(
244+
debounceEvent,
245+
debounceInfo.fn
246+
);
247+
248+
debounceCheck[debounceEvent] = null;
249+
}
267250
}
268-
};
269-
},
270-
[
271-
active,
272-
scrollCheck,
273-
scrollDelay,
274-
scrollThrottle,
275-
resizeCheck,
276-
resizeDelay,
277-
resizeThrottle,
278-
intervalCheck,
279-
intervalDelay,
280-
visibilityCheck
281-
]
282-
);
251+
}
252+
debounceCheckRef.current = null;
283253

284-
return { isVisible, visibilityRect };
254+
if (intervalRef.current) {
255+
intervalRef.current = clearInterval(intervalRef.current);
256+
}
257+
};
258+
}, [
259+
active,
260+
scrollCheck,
261+
scrollDelay,
262+
scrollThrottle,
263+
resizeCheck,
264+
resizeDelay,
265+
resizeThrottle,
266+
intervalCheck,
267+
intervalDelay,
268+
visibilityCheck
269+
]);
270+
271+
return { nodeRef, isVisible, visibilityRect };
285272
}

visibility-sensor.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use strict";
22

3-
import React, { useRef } from "react";
3+
import React from "react";
44
import ReactDOM from "react-dom";
55
import PropTypes from "prop-types";
66
import useVisibilitySensor from "./lib/use-visibility-sensor";
@@ -25,8 +25,7 @@ export default function VisibilitySensor({
2525
offset,
2626
containment
2727
}) {
28-
const nodeRef = useRef();
29-
const { isVisible, visibilityRect } = useVisibilitySensor(nodeRef, {
28+
const { nodeRef, isVisible, visibilityRect } = useVisibilitySensor({
3029
active,
3130
onChange,
3231
partialVisibility,

0 commit comments

Comments
 (0)