Hi guys, in this article we will learn about how to check the position of a component in the viewport, i.e., the viewable area on the mobile screen.
In React Native, you can use the onLayout
prop of a component to determine its visibility on the screen. The onLayout
the prop is called an event object that has a nativeEvent
property containing the layout information for the component.
The onLayout
method is called when a view’s layout is calculated. This typically happens when the view is added to a parent view or when the view’s layout is modified in some way, such as when the device’s screen orientation changes. The onLayout method is passed three parameters:
- A boolean indicating whether the view’s layout has changed
- The left position of the view, in pixels
- The top position of the view, in pixels
The onLayout
method is typically used to determine the size and position of a view and its child’s views. It can be overridden in a custom view to perform custom layout calculations.
But now, we will go one step further and check the component’s visibility at any time, irrespective of the onLayout
method call.
It is helpful if you want to send impression events on the server with information about the section the user sees in your app. It is also helpful in monitoring the ad impressions that are seen by users.
Prerequisite: You should have a basic understanding of mobile app development using React Native and its basic concepts.
Let’s kick off by creating a new React-Native project with the help of this command.
npx react-native init you_project_name
Now let’s create a default screen where we can render our components.
import React from "react"; import { View } from "react-native"; class Home extends React.PureComponent { constructor(props) { super(props); } render() { return <View></View>; } } export default Home;
Now, let’s create three different components: ChildOne, ChildTwo, and ChildThree.class
class ChildOne extends React.PureComponent { constructor(props) { super(props); } render() { return ( <View style={{ padding: 20, borderWidth: 2, justifyContent: "center", alignItems: "center", height: 500, backgroundColor: "yellow", }} > <Text style={{ color: "black" }}>Hi There!</Text> <Text style={{ color: "black" }}>I am child one</Text> </View> ); } }
Let’s have a look at the outcome of the work we’ve done so far!
Now wrap our child with a View component and get the refs of each child component.
To ensure that View exists in the native view hierarchy, set collapsable={false}.
Let’s assume we want to check whether ChildThree is in ViewPort or not.
Let’s now write a function that will notify us when the ChildThree component is in ViewPort.In this function, we will be using measure(callback), which simply determines the location on the screen, height, and width, and returns the value via an async callback.
Method: measure((x, y, width, height, pageX, pageY)) gives us the position of the View.
where x is X offset to frame, y is Y offset to frame and
width = width of the View,
height = height of the View,
pageX = X offset to page,
pageY = Y offset to page.
We are using rectTop and rectBottom to identify the position of the component from the top and bottom of the screen, respectively.
checkIsInViewPort(){ if(this.childThreeRef){ this.childThreeRef.measure((x, y, width, height, pageX, pageY) => { const rectTop = pageX; const rectBottom = pageY + height; const rectWidth = pageX + width; const window = Dimensions.get('window'); const isVisible = rectBottom != 0 && rectTop >= 0 && rectBottom <= window.height && rectWidth > 0 && rectWidth <= window.width; console.log('is in View Port :',isVisible); }) } }
To demonstrate, I’m wrapping these three components inside ScrollView and calling the checkIsInViewPort function on ScrollView’s onScroll; however, this isn’t the best way to handle in-view ports in large applications.
It could be used in a variety of ways depending on the application.
You can take a look at the final code below.
import React from 'react'; import { View, Text, Dimension } from 'react-native'; class HomeScreen extends React.PureComponent { constructor (props) { super(props); } checkIsInViewPort(){ if(this.childThreeRef){ this.childThreeRef.measure((x, y, width, height, pageX, pageY) => { const rectTop = pageX; const rectBottom = pageY + height; const rectWidth = pageX + width; const window = Dimensions.get('window'); const isVisible = rectBottom != 0 && rectTop >= 0 && rectBottom <= window.height && rectWidth > 0 && rectWidth <= window.width; console.log('ChildThree is in View Port ?:',isVisible); }) } } render () { return ( <SafeAreaView> <View style={{ flex: 1, backgroundColor: 'white' }}> <ScrollView onScroll={() => { console.log('scrolling'); this.checkIsInViewPort(); }} > <View collapsable={false} ref={(component) => { this.childOneRef = component; }} > <ChildOne /> </View> <View collapsable={false} ref={(component) => { this.childTwoRef = component; }} > <ChildTwo /> </View> <View collapsable={false} ref={(component) => { this.childThreeRef = component; }} > <ChildThree /> </View> </ScrollView> </View> </SafeAreaView> ); } } export default HomeScreen; class ChildOne extends React.PureComponent { constructor(props){ super(props) } render(){ return( <View style={{padding :20, borderWidth: 2, justifyContent:'center', alignItems:'center', height: 500, backgroundColor: 'yellow'}}> <Text style={{color: 'black'}}>Hi There!</Text> <Text style={{color: 'black'}}>I am child one</Text> </View> ) } } class ChildTwo extends React.PureComponent { constructor(props){ super(props) } render(){ return( <View style={{padding :20, borderWidth: 2, justifyContent:'center', alignItems:'center', height: 500, backgroundColor: 'violet'}}> <Text style={{color: 'white'}}>Hi There!</Text> <Text style={{color: 'white'}}>I am child two</Text> </View> ) } } class ChildThree extends React.PureComponent { constructor(props){ super(props) } render(){ return( <View style={{padding :20, borderWidth: 2, justifyContent:'center', alignItems:'center', height: 500, backgroundColor: 'grey'}}> <Text style={{color: 'white'}}>Hi There!</Text> <Text style={{color: 'white'}}>I am child three</Text> </View> ) } }
Thank You!!