Skip to content

Commit 7adb2be

Browse files
committed
fix(*): ensure that all components are always mounted with a valid default state
1 parent 705697d commit 7adb2be

File tree

4 files changed

+73
-50
lines changed

4 files changed

+73
-50
lines changed

lib/ObserveViewport.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,17 @@ export default class ObserveViewport extends React.Component<IProps, IState> {
9595
this.props.onUpdate(nextViewport, layoutSnapshot);
9696
}
9797

98+
this.syncState(nextViewport);
99+
};
100+
101+
syncState(nextViewport: IState) {
98102
if (this.props.children) {
99103
raf.cancel(this.tickId);
100104
this.tickId = raf(() => {
101105
this.setState(nextViewport);
102106
});
103107
}
104-
};
108+
}
105109

106110
get optionNotifyScroll(): boolean {
107111
return !this.props.disableScrollUpdates;
@@ -115,6 +119,7 @@ export default class ObserveViewport extends React.Component<IProps, IState> {
115119
addViewportChangeListener,
116120
removeViewportChangeListener,
117121
hasRootProviderAsParent,
122+
getCurrentViewport,
118123
}: IContext): React.ReactNode => {
119124
if (!hasRootProviderAsParent) {
120125
warnNoContextAvailable('ObserveViewport');
@@ -147,6 +152,8 @@ export default class ObserveViewport extends React.Component<IProps, IState> {
147152
},
148153
});
149154

155+
this.syncState(getCurrentViewport());
156+
150157
return null;
151158
};
152159

lib/ViewportCollector.tsx

Lines changed: 52 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,7 @@ import {
1313

1414
import { IDimensions, IScroll, IViewport, OnUpdateType } from './types';
1515

16-
const getNodeScroll = (elem = window) => {
17-
let { scrollX, scrollY } = elem;
18-
if (scrollX === undefined) {
19-
scrollX = elem.pageXOffset;
20-
}
21-
if (scrollY === undefined) {
22-
scrollY = elem.pageYOffset;
23-
}
24-
25-
return {
26-
x: scrollX,
27-
y: scrollY,
28-
};
29-
};
30-
31-
const getClientDimensions = (): IDimensions => {
16+
export const getClientDimensions = (): IDimensions => {
3217
if (!document || !document.documentElement) {
3318
return createEmptyDimensionState();
3419
}
@@ -53,6 +38,56 @@ const getClientDimensions = (): IDimensions => {
5338
};
5439
};
5540

41+
const getNodeScroll = (elem = window) => {
42+
let { scrollX, scrollY } = elem;
43+
if (scrollX === undefined) {
44+
scrollX = elem.pageXOffset;
45+
}
46+
if (scrollY === undefined) {
47+
scrollY = elem.pageYOffset;
48+
}
49+
50+
return {
51+
x: scrollX,
52+
y: scrollY,
53+
};
54+
};
55+
56+
export const getClientScroll = (
57+
prevScrollState: IScroll = createEmptyScrollState(),
58+
) => {
59+
if (typeof window === 'undefined') {
60+
return createEmptyScrollState();
61+
}
62+
const { x, y } = getNodeScroll();
63+
const nextScrollState = { ...prevScrollState };
64+
const {
65+
isScrollingLeft: prevIsScrollingLeft,
66+
isScrollingUp: prevIsScrollingUp,
67+
xTurn: prevXTurn,
68+
yTurn: prevYTurn,
69+
} = prevScrollState;
70+
71+
nextScrollState.isScrollingLeft = isScrollingLeft(x, nextScrollState);
72+
nextScrollState.isScrollingRight = !nextScrollState.isScrollingLeft;
73+
74+
nextScrollState.isScrollingUp = isScrollingUp(y, nextScrollState);
75+
nextScrollState.isScrollingDown = !nextScrollState.isScrollingUp;
76+
77+
nextScrollState.xTurn =
78+
nextScrollState.isScrollingLeft === prevIsScrollingLeft ? prevXTurn : x;
79+
nextScrollState.yTurn =
80+
nextScrollState.isScrollingUp === prevIsScrollingUp ? prevYTurn : y;
81+
82+
nextScrollState.xDTurn = x - nextScrollState.xTurn;
83+
nextScrollState.yDTurn = y - nextScrollState.yTurn;
84+
85+
nextScrollState.x = x;
86+
nextScrollState.y = y;
87+
88+
return nextScrollState;
89+
};
90+
5691
const isScrollingLeft = (x: number, prev: IScroll) => {
5792
switch (true) {
5893
case x < prev.x:
@@ -178,37 +213,12 @@ export default class ViewportCollector extends React.PureComponent<IProps> {
178213
};
179214

180215
handleScroll = () => {
181-
const { x, y } = getNodeScroll();
182-
const {
183-
isScrollingLeft: prevIsScrollingLeft,
184-
isScrollingUp: prevIsScrollingUp,
185-
xTurn: prevXTurn,
186-
yTurn: prevYTurn,
187-
} = this.scrollState;
188-
189-
this.scrollState.isScrollingLeft = isScrollingLeft(x, this.scrollState);
190-
this.scrollState.isScrollingRight = !this.scrollState.isScrollingLeft;
191-
192-
this.scrollState.isScrollingUp = isScrollingUp(y, this.scrollState);
193-
this.scrollState.isScrollingDown = !this.scrollState.isScrollingUp;
194-
195-
this.scrollState.xTurn =
196-
this.scrollState.isScrollingLeft === prevIsScrollingLeft ? prevXTurn : x;
197-
this.scrollState.yTurn =
198-
this.scrollState.isScrollingUp === prevIsScrollingUp ? prevYTurn : y;
199-
200-
this.scrollState.xDTurn = x - this.scrollState.xTurn;
201-
this.scrollState.yDTurn = y - this.scrollState.yTurn;
202-
203-
this.scrollState.x = x;
204-
this.scrollState.y = y;
205-
216+
Object.assign(this.scrollState, getClientScroll(this.scrollState));
206217
this.componentMightHaveUpdated = true;
207218
};
208219

209220
handleResize = () => {
210221
Object.assign(this.dimensionsState, getClientDimensions());
211-
212222
this.componentMightHaveUpdated = true;
213223
};
214224

lib/ViewportProvider.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import {
77
IViewportCollectorUpdateOptions,
88
} from './types';
99
import ViewportCollector, {
10-
createEmptyScrollState,
11-
createEmptyDimensionState,
10+
getClientDimensions,
11+
getClientScroll,
1212
} from './ViewportCollector';
1313

1414
interface IProps {
@@ -22,10 +22,10 @@ interface IListener extends IViewportChangeOptions {
2222
skippedIterations: number;
2323
}
2424

25-
const createEmptyViewport = (): IViewport => {
25+
const getCurrentDefaultViewport = (): IViewport => {
2626
return {
27-
scroll: createEmptyScrollState(),
28-
dimensions: createEmptyDimensionState(),
27+
scroll: getClientScroll(),
28+
dimensions: getClientDimensions(),
2929
};
3030
};
3131

@@ -35,7 +35,7 @@ export const ViewportContext = React.createContext({
3535
handler: TViewportChangeHandler,
3636
options: IViewportChangeOptions,
3737
) => {},
38-
getCurrentViewport: createEmptyViewport,
38+
getCurrentViewport: getCurrentDefaultViewport,
3939
hasRootProviderAsParent: false,
4040
version: '__VERSION__',
4141
});
@@ -189,7 +189,7 @@ export default class ViewportProvider extends React.PureComponent<
189189
removeViewportChangeListener: this.removeViewportChangeListener,
190190
getCurrentViewport: () => {
191191
if (!collector.current) {
192-
return createEmptyViewport();
192+
return getCurrentDefaultViewport();
193193
}
194194
return collector.current.getPropsFromState();
195195
},

lib/hooks.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export const useLayoutSnapshot = <T = any>(
8080
recalculateLayoutBeforeUpdate: (viewport: IViewport) => T,
8181
options: IFullOptions = {},
8282
): null | T => {
83+
const { getCurrentViewport } = useContext(ViewportContext);
8384
const [state, setSnapshot] = useState<null | T>(null);
8485
useViewportEffect(
8586
(viewport: IViewport, snapshot: T) => setSnapshot(snapshot),
@@ -88,5 +89,10 @@ export const useLayoutSnapshot = <T = any>(
8889
recalculateLayoutBeforeUpdate,
8990
},
9091
);
92+
93+
useEffect(() => {
94+
setSnapshot(recalculateLayoutBeforeUpdate(getCurrentViewport()));
95+
}, []);
96+
9197
return state;
9298
};

0 commit comments

Comments
 (0)