Skip to content

Commit b3e2959

Browse files
authored
fix: optimize InView (#392)
This changes the rendering flow in the <InView> component, optimizing it for the children. Previously it had a an internal state used to store the current inView and entry values. But it was only really used by the component when using the render props pattern. This lead to unnecessary renders for the plain component. Another optimization tried to limit the state updates, so it would only occur if inView changed. This however meant you wouldn't get the correct entry value in the render prop, since it would only update when inView changes. This would prevent you from getting the latest threshold or isVisible values.
1 parent a9eac7e commit b3e2959

File tree

1 file changed

+13
-15
lines changed

1 file changed

+13
-15
lines changed

src/InView.tsx

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,11 @@ export class InView extends React.Component<
4444
this.unobserve();
4545
this.observeNode();
4646
}
47-
48-
if (prevState.inView !== this.state.inView) {
49-
if (this.state.inView && this.props.triggerOnce) {
50-
this.unobserve();
51-
this.node = null;
52-
}
53-
}
5447
}
5548

5649
componentWillUnmount() {
57-
if (this.node) {
58-
this.unobserve();
59-
this.node = null;
60-
}
50+
this.unobserve();
51+
this.node = null;
6152
}
6253

6354
node: Element | null = null;
@@ -87,8 +78,11 @@ export class InView extends React.Component<
8778

8879
handleNode = (node?: Element | null) => {
8980
if (this.node) {
81+
// Clear the old observer, before we start observing a new element
9082
this.unobserve();
83+
9184
if (!node && !this.props.triggerOnce && !this.props.skip) {
85+
// Reset the state if we get a new node, and we aren't ignoring updates
9286
this.setState({ inView: false, entry: undefined });
9387
}
9488
}
@@ -97,9 +91,13 @@ export class InView extends React.Component<
9791
};
9892

9993
handleChange = (inView: boolean, entry: IntersectionObserverEntry) => {
100-
// Only trigger a state update if inView has changed.
101-
// This prevents an unnecessary extra state update during mount, when the element stats outside the viewport
102-
if (inView !== this.state.inView || inView) {
94+
if (inView && this.props.triggerOnce) {
95+
// If `triggerOnce` is true, we should stop observing the element.
96+
this.unobserve();
97+
}
98+
if (!isPlainChildren(this.props)) {
99+
// Store the current State, so we can pass it to the children in the next render update
100+
// There's no reason to update the state for plain children, since it's not used in the rendering.
103101
this.setState({ inView, entry });
104102
}
105103
if (this.props.onChange) {
@@ -109,8 +107,8 @@ export class InView extends React.Component<
109107
};
110108

111109
render() {
112-
const { inView, entry } = this.state;
113110
if (!isPlainChildren(this.props)) {
111+
const { inView, entry } = this.state;
114112
return this.props.children({ inView, entry, ref: this.handleNode });
115113
}
116114

0 commit comments

Comments
 (0)