Skip to content

Commit 6de61bc

Browse files
committed
feat(hooks): add useLayoutSnapshot
1 parent b0886c6 commit 6de61bc

7 files changed

+54
-13
lines changed

README.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ For detection of some resize events the `ResizeObserver` API is used internally
3636
* [`ViewportProvider`](docs/api/ViewportProvider.md)
3737
* [`ObserveViewport`](docs/api/ObserveViewport_connectViewport_useViewport.md#render-props-event-handler-observeviewport)
3838
* [`connectViewport`](docs/api/ObserveViewport_connectViewport_useViewport.md#hoc-connectviewport)
39-
* [`NEW useViewport`](docs/api/ObserveViewport_connectViewport_useViewport.md#hooks-useviewport-usescroll-usedimensions)
40-
* [`NEW useScroll`](docs/api/ObserveViewport_connectViewport_useViewport.md#hooks-useviewport-usescroll-usedimensions)
41-
* [`NEW useDimensions`](docs/api/ObserveViewport_connectViewport_useViewport.md#hooks-useviewport-usescroll-usedimensions)
39+
* [`NEW useViewport`](docs/api/ObserveViewport_connectViewport_useViewport.md#hooks-useviewport-usescroll-usedimensions-useLayoutSnapshot)
40+
* [`NEW useScroll`](docs/api/ObserveViewport_connectViewport_useViewport.md#hooks-useviewport-usescroll-usedimensions-useLayoutSnapshot)
41+
* [`NEW useDimensions`](docs/api/ObserveViewport_connectViewport_useViewport.md#hooks-useviewport-usescroll-usedimensions-useLayoutSnapshot)
42+
* [`NEW useLayoutSnapshot`](docs/api/ObserveViewport_connectViewport_useViewport.md#hooks-useviewport-usescroll-usedimensions-useLayoutSnapshot)
4243
* [`ObserveBoundingClientRect`](docs/api/ObserveBoundingClientRect.md)
4344
* [Types](docs/api/types.md)
4445

docs/api/ObserveViewport_connectViewport_useViewport.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ render(
8484
);
8585
```
8686

87-
## Hooks: `useViewport`, `useScroll`, `useDimensions`
87+
## Hooks: `useViewport`, `useScroll`, `useDimensions`, `useLayoutSnapshot`
8888

8989
[Hooks](https://reactjs.org/docs/hooks-intro.html) are probably the easiest way to update your components but as of now needs to be consumed with care because this API and hooks in general are in early stage and thus are may contain bugs or will undergo breaking changes. As always, please report bugs if you find some.
9090

@@ -94,8 +94,8 @@ render(
9494

9595
| Property | Type | Required? | Description |
9696
|:---|:---|:---:|:---|
97-
| disableScrollUpdates | boolean | | Disables updates to scroll events |
98-
| disableDimensionsUpdates | boolean | | Disables updates to dimensions events |
97+
| disableScrollUpdates | boolean | | Disables updates to scroll events (only for `useViewport` and `useLayoutSnapshot`) |
98+
| disableDimensionsUpdates | boolean | | Disables updates to dimensions events (only for `useViewport` and `useLayoutSnapshot`) |
9999
| deferUpdateUntilIdle | boolean | | Defers to trigger updates until the collector is idle. See [Defer Events](../concepts/defer_events.md) |
100100
| priority | `'low'`, `'normal'`, `'high'`, `'highest'` | | Allows to set a priority of the update. See [Defer Events](../concepts/scheduler.md) |
101101

docs/concepts/defer_events.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This option is available for
66

77
* ObserveViewport
88
* connectViewport
9-
* useViewport, useScroll, useDimensions
9+
* useViewport, useScroll, useDimensions, useLayoutSnapshot
1010

1111
## Example
1212

docs/concepts/recalculateLayoutBeforeUpdate.md

+7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ The optional `recalculateLayoutBeforeUpdate` property, which accepts a function,
1111
This option is available for
1212

1313
* ObserveViewport
14+
* useLayoutSnapshot
1415

1516
## Example
1617

@@ -19,4 +20,10 @@ This option is available for
1920
recalculateLayoutBeforeUpdate={() => el.getBoundingClientRect()}
2021
onUpdate={({ scroll }, rect) => console.log('Top offset: ', scroll.y + rect.top))}
2122
/>
23+
24+
const Component = () => {
25+
const offsetTop = useLayoutSnapshot(
26+
({ scroll }) => scroll.y + el.getBoundingClientRect().top
27+
);
28+
}
2229
```

examples/index.tsx

+11-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
ObserveViewport,
77
connectViewport,
88
useScroll,
9+
useLayoutSnapshot,
910
useDimensions,
1011
} from '../lib/index';
1112
import StickyScrollUp from './StickyScrollUp';
@@ -26,17 +27,24 @@ const ViewportHeader = connectViewport({ omit: ['scroll'] })<{ a: string }>(
2627
);
2728

2829
const DisplayViewport = () => {
30+
const div = React.useRef(null);
2931
const { x, y } = useScroll({
3032
priority: 'low',
3133
});
3234
const { documentHeight, clientWidth } = useDimensions({
3335
priority: 'low',
3436
});
37+
const offsetTop = useLayoutSnapshot<number>(({ scroll }) => {
38+
if (!div.current) {
39+
return 0;
40+
}
41+
return div.current.getBoundingClientRect().top + scroll.y;
42+
});
3543
return (
36-
<>
44+
<div ref={div}>
3745
x: {x}, y: {y}, documentHeight: {documentHeight}, clientWidth:{' '}
38-
{clientWidth}
39-
</>
46+
{clientWidth}, element offsetTop: {offsetTop}
47+
</div>
4048
);
4149
};
4250

lib/hooks.ts

+22-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import {
88
import { IViewport, IScroll, IDimensions, PriorityType } from './types';
99
import { warnNoContextAvailable } from './utils';
1010

11+
interface IViewPortEffectOptions extends IFullOptions {
12+
recalculateLayoutBeforeUpdate?: (viewport: IViewport) => any;
13+
}
14+
1115
interface IFullOptions extends IOptions {
1216
disableScrollUpdates?: boolean;
1317
disableDimensionsUpdates?: boolean;
@@ -18,11 +22,11 @@ interface IOptions {
1822
priority?: PriorityType;
1923
}
2024

21-
type HandleViewportChangeType = (viewport: IViewport) => void;
25+
type HandleViewportChangeType = (viewport: IViewport, snapshot: any) => void;
2226

2327
const useViewportEffect = (
2428
handleViewportChange: HandleViewportChangeType,
25-
options: IFullOptions,
29+
options: IViewPortEffectOptions,
2630
) => {
2731
const {
2832
addViewportChangeListener,
@@ -42,6 +46,7 @@ const useViewportEffect = (
4246
notifyDimensions: () => !options.disableDimensionsUpdates,
4347
notifyOnlyWhenIdle: () => Boolean(options.deferUpdateUntilIdle),
4448
priority: () => options.priority || 'normal',
49+
recalculateLayoutBeforeUpdate: options.recalculateLayoutBeforeUpdate,
4550
});
4651
return () => removeViewportChangeListener(handleViewportChange);
4752
},
@@ -76,3 +81,18 @@ export const useViewport = (options: IFullOptions = {}): IViewport => {
7681

7782
return state;
7883
};
84+
85+
export const useLayoutSnapshot = <T = any>(
86+
recalculateLayoutBeforeUpdate: (viewport: IViewport) => T,
87+
options: IFullOptions = {},
88+
): null | T => {
89+
const [state, setSnapshot]: [null | T, (state: T) => void] = useState(null);
90+
useViewportEffect(
91+
(viewport: IViewport, snapshot: T) => setSnapshot(snapshot),
92+
{
93+
...options,
94+
recalculateLayoutBeforeUpdate,
95+
},
96+
);
97+
return state;
98+
};

lib/index.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ export {
44
default as ObserveBoundingClientRect,
55
} from './ObserveBoundingClientRect';
66
export { default as ObserveViewport } from './ObserveViewport';
7-
export { useScroll, useDimensions, useViewport } from './hooks';
7+
export {
8+
useScroll,
9+
useDimensions,
10+
useViewport,
11+
useLayoutSnapshot,
12+
} from './hooks';
813
export { IRect, IScroll, IDimensions } from './types';
914

1015
export const VERSION = '__VERSION__';

0 commit comments

Comments
 (0)