React Intersection Observer
Overview
Finding out if an element is visible or where it is concerning its parent element is one of the most challenging tasks on the web. In the past, this meant doing computations brought on by a scroll event, which quickly hurts your app's performance.
Now the react intersection observer API has been introduced which allows us to check for the intersection asynchronously. This API fires a callback when our defined conditions are met.
The fundamental concept is that react intersection observer will watch a DOM element we tell it to watch and will trigger a callback when our predefined requirements are satisfied.
What is React Intersection Observer?
As per the official documentation of the react intersection observer, package is the React implementation of Intersection Observer API. The purpose of this API is to inform us when an element enters or exists in the viewport.
More information about this package is available at the npmjs page.
Features
The react intersection observer package provides us with many features :
- Hooks or Component API
- Better and Optimized performance
- Ease of use as it matches the native API
- Works well with TypeScript
- Testing can be done with Jest
- Tree Shakeable, this means that it only includes the parts that you use
- Small size, around 1.15kb
Installation
The method of installing an npm package remains the same in most cases.
The react intersection observer package can be installed using NPM or Yarn
For NPM :
For Yarn :
Usage of React Intersection Observer
The react intersection observer package has the implementation of Hooks, render props, and plain children. So we can use any of the approaches that are suitable for us. We will discuss them one by one
useInView hook :
The useInView hook makes it very easy to keep a track of our component's inView state. We can also pass additional arguments that are optional while calling the useInView hook.
This hook returns an array that contains :
- a ref
- inView status
- current entry
There is an IntersectionObserverEntry interface inside the Intersection Observer API that describes the intersection between the target element and its root contin
The Intersection Observer API has an interface called InterserctionObserverEntry that describes the intersection between the target element and its root container at any specific moment. The instance of IntersectionObserverEntry is sent to the IntersectionObserver callback in its entries parameter. You can read more about IntersectionObserverEntry from the MDN Docs
To track the entry and exit of an element we attach a ref to that element.
Explanation :
In this code, we have attached a ref to the div that we want to track. Inside the div we are using inView.toString() to render the status of whether the viewport is visible or not. inView is a boolean value that is either true or false.
Render Props :
Render props is a technique of code sharing between the react components through props. Instead of developing its render logic, a component with a render prop invokes a function that produces a React component.
We can use render props with react intersection observer by utilizing the <InView> component. Inside this component, we pass a function that will be called whenever the state changes, i.e, the value of inView changes. Along with the inView prop, the children also get a ref. This ref is attached to the DOM element that we want to track.
Let's look at the code to better understand this :
Plain Children :
In react intersection observer we can pass elements inside of the <InView> ... <InView/> component and these passed elements will be treated as children of <InView/>. Here the <InView/> acts as a wrapping DOM element.
This allows us to pass props like onChange, className, etc to the <InView/> component.
Here is the code sample for this :
API
The following options can be used as props on the <InView/> component or we can use them in the options argument.
Options :
Name | Type | Default | Required | Description |
---|---|---|---|---|
root | Element | document | false | The Element whose limits are considered as the bounding box of the viewport for the element that is the observer's target is identified by the IntersectionObserver interface's read-only root property. The boundaries of the actual document viewport are used if the root is null. |
rootMargin | string | '0px' | false | Margin around the root. This is the same as the CSS margin property. |
threshold | number, number[] | 0 | false | The percentage that should be shown before triggering is indicated by a number between 0 and 1. |
skip | boolean | false | false | This skips creation of the IntersectionObserver |
triggerOnce | boolean | false | false | Only trigger the observer once |
initialView | boolean | false | false | Set initial value of inView boolean. |
fallbackInView | boolean | undefined | false | If the IntersectionObserver API is not working then it throws an Error. |
If you are using the <InView/> component then these props will also work :
Name | Type | Default | Required | Description |
---|---|---|---|---|
as | string | 'div' | false | Render the wrapping element |
children | ({ref, inView, entry}) => React.ReactNode, ReactNode | true | A function is expected for the children | |
onChange | (inView, entry) => void | false | This function is called whenever the inView state changes. |
Testing
The react intersection observer comes with built-in support for testing with Jest and Vitest.
Here is a testing code example :
The IntersectionObserver needs to be mocked to construct meaningful tests.
The react-intersection-observer/test-utils that are included can help with this.
It adds a few methods to help mimic the inView state and mocks the IntersectionObserver.
You have the option of passing a threshold between 0 and 1 or a boolean value when setting the isIntersecting value. It will mimic the actual IntersectionObserver, enabling you to confirm that your components are acting as you would expect them to.
Method | Description |
---|---|
mockAllIsIntersecting(isIntersecting) | On all active IntersectionObserver instances, set the property isIntersecting, the value of isIntersecting should either be a boolean or a limit between 0 and 1. |
mockIsIntersecting(element, isIntersecting) | Set isIntersecting for the IntersectionObserver of a specific element. isIntersecting value should either be a boolean or a limit between 0 and 1. |
intersectionMockInstance(element) | To obtain the mocked IntersectionObserver object, call the intersectionMockInstance method with an element. This allows you to watch and observe procedures secretly. |
setupIntersectionMocking(mockFn) | Call this function in beforeEach to mock the intersection observer so we can interact with it in testing. |
resetIntersectionMocking() | Reset the mocks on IntersectionObserver - Should be called in afterEach. |
Using Intersection Observer in a React Hook
We can create a custom hook for the IntersectionObserver. Creating a custom hooks us several benefits like re-usability and clean code. We can simply create a hook an use it anywhere in our entire application in a very clean way.
Here are the steps that we need to follow to create the custom hook :
-
Create a function called useIntersect such that it takes in options as an argument.
-
Inside this function, we have to
-
Create a ref using the useRef hook to store the reference of the element that we want to observe.
-
Create a state isVisible to know whenever our element is inside the viewport.
-
We need to make a callback function that will get IntersectionObserverEntries as an argument and inside it, we will check if the first entry intersects with the viewport. If there is an intersection then we will call the setIsVisible to set true or else false.
-
An object named options
-
Now we will utilize the useEffect hook and create an observer using all the methods and objects we created.
-
-
The functionuseIntersect will return [isVisible, containerRef]
-
Attach the ref to the element we want to track.
-
Now we can utilize this hook we just created
This is how we can easily use our customHook anywhere in our application.
FAQs
There are some Frequently Asked Questions (FAQ!) that we will discuss here :
Q How to Assign Multiple Refs to a Component?
A Multiple ref assignments can be wrapped in a single useCallback
Conclusion
- IntersectionObserver API helps us to observe a DOM element and it fires a callback whenever our defined conditions are met.
- react intersection observer package is the react implementation of the IntersectionObserver API.
- This package allows us to use the API with hooks, render props, and even plain children.
- The API has a lot of parameters that can be used to customize the behavior of this package.
- react intersection observer has built-in testing support for Jest and Vitest.
- We can make our custom hook for the implementation of the IntersectionObserver API.