Skip to content

Commit

Permalink
feat(ObserveViewport): allow to perform layouts reads in one badge fo…
Browse files Browse the repository at this point in the history
…r all rendered components before updating
  • Loading branch information
garthenweb committed Sep 22, 2018
1 parent 674d03f commit 318da48
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 10 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,21 @@ render(
);
```

When an update is triggered, sometimes further calculations on the DOM which might trigger [layouts/ reflows](https://gist.github.com/paulirish/5d52fb081b3570c81e3a) are required to execute a task.
In general the best performance is archive by first reading all the values in one badge and later update the DOM again. With multiple components in one page this can become difficult.

The optional `recalculateLayoutBeforeUpdate` property, which accepts a function, will allow to exactly handle those reads in one badge for all components to later perform the update:

* first all `recalculateLayoutBeforeUpdate` functions for all components are executed.
* second all `onUpdate` function are called which receive the value returned from `recalculateLayoutBeforeUpdate` as the second argument.

``` javascript
<ObserveViewport
recalculateLayoutBeforeUpdate={() => el.getBoundingClientRect()}
onUpdate={({ scroll }, rect) => console.log('Top offset: ', scroll.y + rect.top))}
/>
```

#### Omit events

In case only certain updates are required `connectViewport` allows an `omit` option to skip updates to `scroll` and `dimensions` events. If both strings are included within the `omit` array, no events will get triggered at all.
Expand Down
9 changes: 6 additions & 3 deletions lib/ObserveViewport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ interface IState extends IChildProps {}

interface IProps {
children?: (props: IChildProps) => React.ReactNode;
onUpdate?: (props: IChildProps) => void;
onUpdate?: (props: IChildProps, layoutSnapshot: any) => void;
recalculateLayoutBeforeUpdate?: (props: IChildProps) => any;
disableScrollUpdates: boolean;
disableDimensionsUpdates: boolean;
}
Expand Down Expand Up @@ -84,7 +85,7 @@ export default class ObserveViewport extends React.Component<IProps, IState> {
raf.cancel(this.tickId);
}

handleViewportUpdate = (viewport: IViewport) => {
handleViewportUpdate = (viewport: IViewport, layoutSnapshot: any) => {
const scroll = this.props.disableScrollUpdates ? null : viewport.scroll;
const dimensions = this.props.disableDimensionsUpdates
? null
Expand All @@ -95,7 +96,7 @@ export default class ObserveViewport extends React.Component<IProps, IState> {
};

if (this.props.onUpdate) {
this.props.onUpdate(nextViewport);
this.props.onUpdate(nextViewport, layoutSnapshot);
}

this.tickId = raf(() => {
Expand All @@ -119,12 +120,14 @@ export default class ObserveViewport extends React.Component<IProps, IState> {
this.removeViewportChangeListener(this.handleViewportUpdate, {
notifyScroll: !this.props.disableScrollUpdates,
notifyDimensions: !this.props.disableDimensionsUpdates,
recalculateLayoutBeforeUpdate: this.props.recalculateLayoutBeforeUpdate,
});
}
this.removeViewportChangeListener = removeViewportChangeListener;
addViewportChangeListener(this.handleViewportUpdate, {
notifyScroll: !this.props.disableScrollUpdates,
notifyDimensions: !this.props.disableDimensionsUpdates,
recalculateLayoutBeforeUpdate: this.props.recalculateLayoutBeforeUpdate,
});
return null;
};
Expand Down
22 changes: 16 additions & 6 deletions lib/ViewportProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -238,13 +238,23 @@ export default class ViewportProvider extends React.PureComponent {

if (scrollDidUpdate || dimensionsDidUpdate) {
const publicState = this.getPropsFromState();
this.listeners.forEach(({ handler, notifyScroll, notifyDimensions }) => {
if (
const updatableListeners = this.listeners.filter(
({ notifyScroll, notifyDimensions }) =>
(notifyScroll && scrollDidUpdate) ||
(notifyDimensions && dimensionsDidUpdate)
) {
handler(publicState);
}
(notifyDimensions && dimensionsDidUpdate),
);
const layouts = updatableListeners.map(
({ recalculateLayoutBeforeUpdate }) => {
if (recalculateLayoutBeforeUpdate) {
return recalculateLayoutBeforeUpdate(publicState);
}
return null;
},
);

updatableListeners.forEach(({ handler }, index) => {
const layout = layouts[index];
handler(publicState, layout);
});
}
};
Expand Down
4 changes: 3 additions & 1 deletion lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@ export interface IViewport {
}

export type TViewportChangeHandler = (
{ scroll, dimensions }: IViewport,
viewport: IViewport,
layoutSnapshot: any,
) => void;

export interface IViewportChangeOptions {
notifyScroll: boolean;
notifyDimensions: boolean;
recalculateLayoutBeforeUpdate?: (viewport: IViewport) => any;
}

0 comments on commit 318da48

Please sign in to comment.