Skip to content

Commit 0252048

Browse files
authoredSep 9, 2020
fix: check intersectionRatio in addition to isIntersecting (#384)
1 parent 2822252 commit 0252048

File tree

6 files changed

+180
-167
lines changed

6 files changed

+180
-167
lines changed
 

‎package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@
139139
"@babel/preset-flow": "^7.9.0",
140140
"@babel/preset-react": "^7.9.4",
141141
"@babel/preset-typescript": "^7.9.0",
142-
"@emotion/react": "11.0.0-next.15",
142+
"@emotion/cache": "^11.0.0-next.16",
143+
"@emotion/react": "^11.0.0-next.16",
143144
"@storybook/addon-actions": "^6.0.21",
144145
"@storybook/addon-viewport": "^6.0.21",
145146
"@storybook/react": "^6.0.21",

‎src/InView.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,7 @@ export class InView extends React.Component<
9696
this.observeNode();
9797
};
9898

99-
handleChange = (entry: IntersectionObserverEntry) => {
100-
const inView = entry.isIntersecting || false;
99+
handleChange = (inView: boolean, entry: IntersectionObserverEntry) => {
101100
// Only trigger a state update if inView has changed.
102101
// This prevents an unnecessary extra state update during mount, when the element stats outside the viewport
103102
if (inView !== this.state.inView || inView) {

‎src/index.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export default InView;
77
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
88

99
export type ObserverInstanceCallback = (
10+
inView: boolean,
1011
entry: IntersectionObserverEntry,
1112
) => void;
1213

‎src/observers.ts

+14-3
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,25 @@ function createObserver(options: IntersectionObserverInit) {
5555

5656
const observer = new IntersectionObserver((entries) => {
5757
entries.forEach((entry) => {
58-
// @ts-ignore
58+
// While it would be nice if you could just look at isIntersecting to determine if the component is inside the viewport, browsers can't agree on how to use it.
59+
// -Firefox ignores `threshold` when considering `isIntersecting`, so it will never be false again if `threshold` is > 0
60+
const inView = observer.thresholds.some((threshold) => {
61+
return !entry.isIntersecting
62+
? // The intersectionRatio should be more than the threshold to be considered inside the viewport
63+
entry.intersectionRatio > threshold
64+
: // If we're not intersecting, make sure we accept `intersectionRatio` 0 as not inside the viewport
65+
entry.intersectionRatio >= threshold;
66+
});
67+
68+
// @ts-ignore support IntersectionObserver v2
5969
if (options.trackVisibility && typeof entry.isVisible === 'undefined') {
6070
// The browser doesn't support Intersection Observer v2, falling back to v1 behavior.
6171
// @ts-ignore
62-
entry.isVisible = true;
72+
entry.isVisible = inView;
6373
}
74+
6475
elements.get(entry.target)?.forEach((callback) => {
65-
callback(entry);
76+
callback(inView && entry.isIntersecting, entry);
6677
});
6778
});
6879
}, options);

‎src/useInView.tsx

+14-11
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,21 @@ import * as React from 'react';
33
import { InViewHookResponse, IntersectionOptions } from './index';
44
import { useEffect } from 'react';
55
import { observe } from './observers';
6+
type State = {
7+
inView: boolean;
8+
entry?: IntersectionObserverEntry;
9+
};
10+
11+
const initialState: State = {
12+
inView: false,
13+
entry: undefined,
14+
};
615

716
export function useInView(
817
options: IntersectionOptions = {},
918
): InViewHookResponse {
1019
const unobserve = React.useRef<Function>();
11-
const [intersectionEntry, setIntersectionEntry] = React.useState<
12-
IntersectionObserverEntry | undefined
13-
>(undefined);
20+
const [state, setState] = React.useState<State>(initialState);
1421

1522
const setRef = React.useCallback(
1623
(node) => {
@@ -26,8 +33,8 @@ export function useInView(
2633
if (node) {
2734
unobserve.current = observe(
2835
node,
29-
(entry) => {
30-
setIntersectionEntry(entry);
36+
(inView, entry) => {
37+
setState({ inView, entry });
3138

3239
if (
3340
entry.isIntersecting &&
@@ -58,15 +65,11 @@ export function useInView(
5865
if (!unobserve.current && !options.triggerOnce && !options.skip) {
5966
// If we don't have a ref, then reset the state (unless the hook is set to only `triggerOnce` or `skip`)
6067
// This ensures we correctly reflect the current state - If you aren't observing anything, then nothing is inView
61-
setIntersectionEntry(undefined);
68+
setState(initialState);
6269
}
6370
});
6471

65-
const result = [
66-
setRef,
67-
intersectionEntry ? intersectionEntry.isIntersecting : false,
68-
intersectionEntry,
69-
] as InViewHookResponse;
72+
const result = [setRef, state.inView, state.entry] as InViewHookResponse;
7073

7174
// Support object destructuring, by adding the specific values.
7275
result.ref = result[0];

0 commit comments

Comments
 (0)