Package Exports
- react-native-appear-observer
- react-native-appear-observer/src/index.ts
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (react-native-appear-observer) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
react-native-appear-observer
A React Native library that helps to track the appearance of an element on the screen.
Description
The library's functionality is based on the loop that measures element's position on the screen and looks if it lies within the boundaries of the designated parent.
The loop is intended to operate only when user interaction has happened. This is achieved by attaching interaction event listeners to the parent element relative to which the tracking is performed. When app goes idle the loop stops after a delay.
API is designed in a way that provides easy basic usage without boilerplate and cluttering the code, for that all components try to make their setup and all necessary attachments automatic.
The basic intended usage implies setting a ScrollView or any List component as parent and tracking the element relative to it.
As well the library allows other types of usage and provides a wide variety of settings and customizations to cover different usage scenarios.
Among the options are:
- Ability to use with and without a context wrapping the parent element.
- To set custom threshold for element's appearance, in percents of the element's size.
- To record custom interactions as such that trigger the start of the tracking.
- To disable interaction mode and run the loop indefinitely, as well as an ability to manually start and stop it.
- Usage without a parent, with tracking relative to the screen, and an option to set static offsets from the screen boundaries.
- Different optimizations like ability to set a custom delay between the loop cycles, disable parent boundaries recalculation.
Usage
Basic
All setup options, except for measuring relative to window, consist of two steps: setting parent component relative to which the tracking will be performed and child observed components.
Interaction mode with provider
Wrap the parent component with provider. The provider will attach all necessary props automatically to its child component and does not require any configuration in basic setup. Works perfectly with ScrollView and virtualized lists.
Set up the useAppearObserver hook for the element you want to track and pass it the props returned by the hook. It attaches a ref to the component, based on which the measurement will run, and adds specific props to prevent the element from collapsing which prevents measurement and tracking.
const App = () => {
return (
<AppearObserverProvider>
<ScrollView>
<TrackedComponent />
</ScrollView>
</AppearObserverProvider>
)
}
const TrackedComponent = () => {
const { refProps } = useAppearObserver({
onAppear: useCallback(() => console.log('Hola!'), [])
})
return <View { ...refProps } />
}
Interaction mode without provider
Attach ref and interaction handlers to the parent component, pass interaction listeners and parent ref to the tracked component.
const App = () => {
const scrollViewRef = useRef()
const { interactionHandlers, interactionListeners } = useInteractionManager()
return (
<ScrollView ref={scrollViewRef} { ...interactionHandlers }>
<TrackedComponent parentRef={scrollViewRef} interactionListeners={interactionListeners} />
</ScrollView>
)
}
const TrackedComponent = ({ parentRef, interactionListeners }: any) => {
const { refProps } = useAppearObserver({
parentRef,
interactionListeners,
onAppear: useCallback(() => console.log('Hi there!'), [])
})
return <View {...refProps} />
}
Static mode with measurement relative to parent
Attach ref to the parent component. Pass parent ref to the observed component. Set interactionModeEnabled to false.
const App = () => {
const scrollViewRef = useRef()
return (
<ScrollView ref={scrollViewRef}>
<TrackedComponent parentRef={scrollViewRef} />
</ScrollView>
)
}
const TrackedComponent = ({ parentRef }: any) => {
const { refProps } = useAppearObserver({
parentRef,
onAppear: useCallback(() => console.log('Hi there!'), []),
options: {
interactionModeEnabled: false,
}
})
return <View {...refProps} />
}
Static mode with measurement relative to window
Set up useAppearObserver with useScreenIfNoParent set to true. This mode also supports parentOffsets.
const App = () => {
return (
<ScrollView>
<TrackedComponent />
</ScrollView>
)
}
const TrackedComponent = () => {
const { refProps } = useAppearObserver({
onAppear: useCallback(() => console.log('Hi there!'), []),
options: {
interactionModeEnabled: false,
useScreenIfNoParent: true,
}
})
return <View {...refProps} />
}
Specific cases
Usage with react navigation
Set 'enabled' flag to isFocused from React Navigation. By default, the loop stops when the screen goes out of focus and won't start again otherwise, so it's necessary to start it manually.
Depending on your needs, as an option, you may choose to pass onDisappear callback to the onDisable slot. This way the the hook will report disappearance when screen goes out of focus and the tracked element becomes not visible.
const TrackedElement = ({ onAppear, onDisappear }: any) => {
const isFocused = useIsFocused()
const { refProps } = useAppearObserver({
enabled: isFocused,
onAppear,
onDisappear,
onDisable: onDisappear // Optional
})
return <View {...refProps} style={elementStyle} />
}
Custom interaction handlers setup
This example shows how to use provider component's support of children as render prop to manually assign all necessary props to the underlying components.
The same can be achieved without the usage of provider, by following the example in the Interaction mode without provider section.
This is useful when you need set custom component as parent, set some actions as interaction start/end for the tracking loop, for example clicking a 'Scroll to top' button.
The example shows all possible props the provider gives, you can pick only what you need and spread the rest.
const App = () => {
return (
<AppearObserverProvider>
{
({
collapsable,
onLayout,
onScroll,
onScrollBeginDrag,
onScrollEndDrag,
onTouchStart,
onTouchEnd,
onTouchMove,
onTouchCancel,
onMomentumScrollEnd,
interactionRecorders,
}) => (
<>
<ScrollView
collapsable={collapsable}
onLayout={onLayout}
onScrollBeginDrag={onScrollBeginDrag}
onScrollEndDrag={onScrollEndDrag}
onTouchStart={onTouchStart}
onTouchEnd={onTouchEnd}
onTouchMove={onTouchMove}
onTouchCancel={onTouchCancel}
onMomentumScrollEnd={onMomentumScrollEnd}
>
<TrackedComponent />
</ScrollView>
<ActionButton title="Scroll to top" onPress={() => {
interactionRecorders.recordInteractionStart()
scrollToTop()
}} />
</>
)
}
</AppearObserverProvider>
)
}
Optimisations
There are a few technics to optimize the tracking loop.
- recalculateParentBoundaries can be set to false if the parent component's size and position, or the component itself, doesn't change. The observer will cache the first measured boundaries in this case.
- intervalDelay can be adjusted for less frequent loop cycles.
- enabled can be set to false when tracking becomes unnecessary.
API
useAppearObserver
The core hook that performs tracking of the component by running a loop measuring the component's position.
Props
Prop | Description | Default value |
---|---|---|
elementRef | External ref to use instead of the one created by the hook. Use if you need to work with the tracked element by ref. No ref merging happens, the external one overrides the internal default. | - |
parentRef | Optional props for usage without context. Provides parent element ref directly to the hook, overrides the ref supplied by context. | - |
onAppear | Callback that triggers when the element comes into visibility. | - |
onDisappear | Callback that triggers when the element disappears. | - |
onEnable | Callback that fires when tracking gets enabled. | - |
onDisable | Callback that fires when tracking gets disabled. | - |
enabled | Flag which toggles tracking on and off. Useful when tracking needs to be reset, like in case with react navigation. | true |
interactionListeners | Slot for interaction listeners passed from parent, for usage without context. | - |
Options
Option | Description | Default value |
---|---|---|
visibilityThreshold | Defines what part of an element should be visible for it to trigger callback, from 0 to 1. | 0.001 |
intervalDelay | Determines a delay in milliseconds between visibility check repetitions. | 50 |
recalculateParentBoundaries | Tells whether observer should measure parent element boundaries on every on every check or measure once and cache. | true |
parentOffsets | Sets additional static offsets added to the calculated parent boundaries. Useful if you need to consider the height of some header inside a ScrollView. | 0 for each corner |
interactionModeEnabled | Makes the tracking loop fire only when user interaction has happened. When disabled, the loop runs indefinitely until stopped by enabled prop. | true |
useScreenIfNoParent | Provides an option to track the element relative to screen instead of parent element. Only works if parent ref is not passed. | false |
optimizeOutOfScreen | Doubles the delay between loop cycles if the element is out of screen. A small optimization, candidate for improvement. | true |
Returns
Prop | Description |
---|---|
refProps | Ref and props that prevent element from being collapsed, see useObservableTargetRef |
restart | A handler to manually restart the observer. Can be used to trigger onAppear manually, for example when element's visibility hasn't changed, but its content has. |
AppearObserverProvider
Component that provides a parent ref and interaction listeners through context for observers down the tree.
Modifies the props of its child component by attaching event listeners to interaction callbacks like onScroll.
Component is designed to provide minimalistic default usage, so it makes all arrangements under the hood. By default, it attaches all kinds of event listeners to the child element, with ScrollView in mind, but the element may not support some of them and these events will be ignored, or have different event callbacks. For manual setup provide children as render prop and arrange all listeners manually.Props
Prop | Description | Default value |
---|---|---|
enableInteractionMode | When true, the touch handlers are attached to the child element of the provider, and child observers run checks only upon touch interactions, stopping them after a period of inactivity. | true |
ref | An external ref to pass to the provider's child element. Provider sets it's own ref by default, but can use external one for its operations. No ref merging happens, the external one overrides the internal default. | _ |
children | React element or render prop for manual setup. | _ |
offsets | Additional static offsets added to the parent element's boundaries. Can be used to account for ScrollView headers in calculations. | 0 for each corner |
onLayout, onScroll etc | Callbacks to be passed to the child component on top of the provider's internal ones. When an element is wrapped with provider, you have to pass these callbacks to the provider instead of the target element, otherwise provider overwrites them. | - |
useInteractionManager
A hook that returns interaction event handlers and interaction listeners for attaching them to the parent component and observers respectfully.
Used inside Provider, but exposed for setting up interaction mode tracking without provider.
Props
Prop | Description | Default value |
---|---|---|
onScroll, onScrollBeginDrag, onScrollEndDrag etc | External listeners to be attached alongside internal service ones. | true |
Returns
Prop | Description |
---|---|
interactionHandlers | Callbacks for interaction events that trigger to be attached to parent component. They trigger the tracking loop start. |
interactionRecorders | Separate callbacks that can be attached to any event to mark it as interaction start or end. For example can serve to set 'Scroll to top' button press as interaction. |
interactionListeners | Interaction listeners to be passed to useAppearObserver hook of the tracked components so that they can listen to parent interactions. |
useObservableTargetRef
A hook that incapsulates props necessary to prevent element from being collapsed, and a ref. Can be used to attach these props to the parent component in no provider, if it can be collapsed.
Props
Prop | Description | Default value |
---|---|---|
ref | External ref to be used instead of the default one set up internally. | - |
Returns
Prop | Description |
---|---|
ref | A regular React ref |
collapsable | Set to true, prevents native node from being collapsed. |
onLayout | Also serves for collapse prevention, as collapsable is currently only for Android. |