Skip to content

Commit

Permalink
feat(hooks): add hooks effect handler for side effect handling
Browse files Browse the repository at this point in the history
  • Loading branch information
garthenweb committed Jun 22, 2019
1 parent f4345f2 commit 18f2eae
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 39 deletions.
27 changes: 23 additions & 4 deletions examples/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import {
useLayoutSnapshot,
useDimensions,
useRect,
useScrollEffect,
useDimensionsEffect,
useViewportEffect,
useRectEffect,
} from '../lib/index';
import StickyScrollUp from './StickyScrollUp';
import Sticky from './Sticky';
Expand Down Expand Up @@ -107,17 +111,17 @@ class Example extends React.PureComponent<{}, { disabled: boolean }> {
<ObserveViewport
disableDimensionsUpdates
onUpdate={props => {
console.log('update scroll only', props.scroll);
console.log('ObserveViewport: update scroll only', props.scroll);
}}
/>
<ObserveViewport
onUpdate={({ dimensions, scroll }) => {
if (this.lastDimensions !== dimensions) {
console.log('update dimensions', dimensions);
console.log('ObserveViewport: update dimensions', dimensions);
this.lastDimensions = dimensions;
}
if (this.lastScroll !== scroll) {
console.log('update scroll', scroll);
console.log('ObserveViewport: update scroll', scroll);
this.lastScroll = scroll;
}
}}
Expand All @@ -126,7 +130,10 @@ class Example extends React.PureComponent<{}, { disabled: boolean }> {
deferUpdateUntilIdle
disableScrollUpdates
onUpdate={props => {
console.log('update dimensions lazy', props.dimensions);
console.log(
'ObserveViewport: update dimensions lazy',
props.dimensions,
);
}}
/>
<Placeholder />
Expand All @@ -138,10 +145,22 @@ class Example extends React.PureComponent<{}, { disabled: boolean }> {
}
}

const HooksExample = () => {
const ref = React.useRef<HTMLDivElement>();
useScrollEffect(scroll => console.log('hook:scroll effect', scroll));
useDimensionsEffect(dimensions =>
console.log('hook:dimensions effect', dimensions),
);
useViewportEffect(viewport => console.log('hook:viewport effect', viewport));
useRectEffect(rect => console.log('hook:rect effect', rect), ref);
return <div ref={ref} />;
};

render(
<ViewportProvider experimentalSchedulerEnabled>
<main role="main">
<Example />
<HooksExample />
<Placeholder />
<Placeholder />
<Placeholder />
Expand Down
100 changes: 65 additions & 35 deletions lib/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
import {
useContext,
useEffect,
useLayoutEffect,
useState,
RefObject,
} from 'react';
import { useContext, useEffect, useState, RefObject } from 'react';

import { ViewportContext } from './ViewportProvider';
import { IViewport, IScroll, IDimensions, PriorityType, IRect } from './types';
import { warnNoContextAvailable } from './utils';

interface IViewPortEffectOptions extends IFullOptions {
recalculateLayoutBeforeUpdate?: (viewport: IViewport) => any;
interface IViewPortEffectOptions<T> extends IFullOptions {
recalculateLayoutBeforeUpdate?: (viewport: IViewport) => T;
}

interface IFullOptions extends IOptions {
Expand All @@ -23,15 +17,15 @@ interface IOptions {
deferUpdateUntilIdle?: boolean;
priority?: PriorityType;
}
interface IEffectOptions<T> extends IOptions {
recalculateLayoutBeforeUpdate?: (viewport: IViewport) => T;
}

type HandleViewportChangeType = (options: {
viewport: IViewport;
snapshot: any;
}) => void;
type HandleViewportChangeType = (viewport: IViewport, snapshot: any) => void;

const useViewportEffect = (
export const useViewportEffect = <T = any>(
handleViewportChange: HandleViewportChangeType,
options: IViewPortEffectOptions,
options: IViewPortEffectOptions<T> = {},
) => {
const {
addViewportChangeListener,
Expand All @@ -45,19 +39,38 @@ const useViewportEffect = (
}

useEffect(() => {
const handler = (viewport: IViewport, snapshot: any) =>
handleViewportChange({ viewport, snapshot });
addViewportChangeListener(handler, {
addViewportChangeListener(handleViewportChange, {
notifyScroll: () => !options.disableScrollUpdates,
notifyDimensions: () => !options.disableDimensionsUpdates,
notifyOnlyWhenIdle: () => Boolean(options.deferUpdateUntilIdle),
priority: () => options.priority || 'normal',
recalculateLayoutBeforeUpdate: options.recalculateLayoutBeforeUpdate,
});
return () => removeViewportChangeListener(handler);
return () => removeViewportChangeListener(handleViewportChange);
}, [addViewportChangeListener, removeViewportChangeListener]);
};

export const useViewport = (options: IFullOptions = {}): IViewport => {
const { getCurrentViewport } = useContext(ViewportContext);
const [state, setViewport] = useState(getCurrentViewport());
useViewportEffect(viewport => setViewport(viewport), options);

return state;
};

export const useScrollEffect = <T = any>(
effect: (scroll: IScroll, snapshot: T) => void,
options: IEffectOptions<T> = {},
) => {
useViewportEffect(
(viewport, snapshot: T) => effect(viewport.scroll, snapshot),
{
disableDimensionsUpdates: true,
...options,
},
);
};

export const useScroll = (options: IOptions = {}): IScroll => {
const { scroll } = useViewport({
disableDimensionsUpdates: true,
Expand All @@ -67,6 +80,19 @@ export const useScroll = (options: IOptions = {}): IScroll => {
return scroll;
};

export const useDimensionsEffect = <T = any>(
effect: (scroll: IDimensions, snapshot: T) => void,
options: IEffectOptions<T> = {},
) => {
useViewportEffect(
(viewport, snapshot: T) => effect(viewport.dimensions, snapshot),
{
disableScrollUpdates: true,
...options,
},
);
};

export const useDimensions = (options: IOptions = {}): IDimensions => {
const { dimensions } = useViewport({
disableScrollUpdates: true,
Expand All @@ -76,12 +102,26 @@ export const useDimensions = (options: IOptions = {}): IDimensions => {
return dimensions;
};

export const useViewport = (options: IFullOptions = {}): IViewport => {
const { getCurrentViewport } = useContext(ViewportContext);
const [state, setViewport] = useState(getCurrentViewport());
useViewportEffect(({ viewport }) => setViewport(viewport), options);
export const useRectEffect = (
effect: (rect: IRect | null) => void,
ref: RefObject<HTMLElement>,
options?: IFullOptions,
) => {
useViewportEffect((_, snapshot) => effect(snapshot), {
...options,
recalculateLayoutBeforeUpdate: () =>
ref.current ? ref.current.getBoundingClientRect() : null,
});
};

return state;
export const useRect = (
ref: RefObject<HTMLElement>,
options?: IFullOptions,
): IRect | null => {
return useLayoutSnapshot(
() => (ref.current ? ref.current.getBoundingClientRect() : null),
options,
);
};

export const useLayoutSnapshot = <T = any>(
Expand All @@ -90,7 +130,7 @@ export const useLayoutSnapshot = <T = any>(
): null | T => {
const { getCurrentViewport } = useContext(ViewportContext);
const [state, setSnapshot] = useState<null | T>(null);
useViewportEffect(({ snapshot }: { snapshot: T }) => setSnapshot(snapshot), {
useViewportEffect((_, snapshot: T) => setSnapshot(snapshot), {
...options,
recalculateLayoutBeforeUpdate,
});
Expand All @@ -101,13 +141,3 @@ export const useLayoutSnapshot = <T = any>(

return state;
};

export const useRect = (
ref: RefObject<HTMLElement>,
options?: IFullOptions,
): IRect | null => {
return useLayoutSnapshot(
() => (ref.current ? ref.current.getBoundingClientRect() : null),
options,
);
};
4 changes: 4 additions & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ export {
export { default as ObserveViewport } from './ObserveViewport';
export {
useScroll,
useScrollEffect,
useDimensions,
useDimensionsEffect,
useViewport,
useViewportEffect,
useLayoutSnapshot,
useRect,
useRectEffect,
} from './hooks';
export { IRect, IScroll, IDimensions } from './types';

Expand Down

0 comments on commit 18f2eae

Please sign in to comment.