Cómo usar Intersection Observer API en Next.js

28/09/2021

¡Hola! En este artículo vamos a ver cómo usar Intersection Observer API. Es una API del navegador que nos permite detectar cuando un elemento es visible en pantalla, o (y acá está lo bueno) cuando el elemento está por serlo, y con esto poder realizar alguna acción.

Hay varios casos de uso para esta API, como lazy loading de imágenes o implementar "infinite scrolling" pero en este artículo vamos a usar esta API para interactuar con elementos de nuestra UI a medida que el usuario va haciendo scroll.

En concreto 🧐 nuestro proyecto va a tener una serie de artículos y a medida que vamos avanzando por ellos, vamos a ir marcando como "activo" el título que corresponda.

Te dejo links a la demo del proyecto y al repositorio completo del proyecto en GitHub.

Empecemos

Lo primero que tenemos que hacer es crear nuestro proyecto en Next.js, y eso lo hacemos con el comando yarn create next-app

Una vez que tenemos nuestro proyecto corriendo vamos a crear un directorio /components en el cual vamos a agregar dos componentes: Title y Article. De tal manera que queden así:

Title.js

import React from 'react'
import styles from '../styles/Title.module.css'

const Title = ({text, isActive}) => {
    return <li className={`${styles.title} ${isActive ? styles.active : ''}`}>{text}</li>
}
export default Title

Article.js

import React from 'react'
import styles from '../styles/Article.module.css'

const Article = ({ id, title, content }) => {
   return (
       <article className={styles.article} id={id}>
           <h2>{title}</h2>
           <p className={styles.content}>{content}</p>
       </article>
   )
}

export default Article

Usando nuestros componentes

Una vez que tenemos nuestros componentes, vamos a usarlos en el archivo index.js de tal manera que quede así:

import React, { useState } from 'react'
import Head from 'next/head'
import styles from '../styles/Home.module.css'

import Title from '../components/Title'
import Article from '../components/Article'

import data from '../data/data.json'

export default function Home() {
  const [activeArticle, setActiveArticle] = useState('01');
  return (
    <main>
      <ul>
        {data.titles.map(({title, id}) => {
          return <Title key={id} text={title} isActive={id === activeArticle} />
        })}
      </ul>
      <div>
        {data.articles.map(({id, title, content}) => {
          return <Article key={id} title={title} content={content} id={id} />
        })}
      </div>
    </main>
)}

Agregando la Intersection Observer API

Lo primero que tenemos que hacer es crear, en index.js, nuestro observer, el cual recibe dos parámetros:

  • El primero es el call back que queremos ejecutar cuando la API detecte que el scroll está cerca del elemento a observar;

  • El segundo es un objeto que nos permite configurar nuestro observer, podemos indicar qué tan pronto o tarde disparar nuestro call back, es decir, con qué porcentaje de visibilidad de nuestro elemento se debe ejecutar el call back.

Una vez creado nuestro observer, tenemos que indicarle qué elementos observar. Y como nuestro proyecto tiene varios artículos a observar, cada uno de ellos va a recibir por prop un observer, de tal manera que nuestro index.js quede así:

// imports..

export default function Home() {
  const [activeArticle, setActiveArticle] = useState('01');

  const handleIntersect = (entries) => {
    if (entries[0].isIntersecting) {
      setActiveArticle(entries[0].target.id);
    }
  };

  const createObserver = (target) => {
    const options = { threshold: 1.0 };

    const observer = new IntersectionObserver(handleIntersect, options);

    observer.observe(target);
  };

  return (
    <main>
      <ul>
        {data.titles.map(({title, id}) => {
          return <Title key={id} text={title} isActive={id === activeArticle} />
        })}
      </ul>
      <div>
        {data.articles.map(({id, title, content}) => {
          return <Article key={id} title={title} content={content} id={id} createObserver={createObserver} />
        })}
      </div>
    </main>
)}

Analicemos este código 🤔 .

La función handleIntersect es nuestro call back, esto quiere decir que es la función que se va a ejecutar cada vez que observer detecte que el elemento observado sea en visible.

Lo que hace este call back es tomar el ID del elemento observado y guardarlo en el state de la aplicación, el cual nos sirve para saber qué artículo es visible y así marcar como activo el título correspondiente.

Ya tenemos nuestro observer y call back creados, ahora solo nos queda usarlos. Y para eso, como dijimos anteriormente, debemos enviar por propiedad nuestro observer a cada artículo y ejecutarlo. Nuestro Article.js nos quedará de la siguiente manera:

import React, { useEffect, useRef } from 'react'
import styles from '../styles/Article.module.css'

const Article = ({ id, title, content, createObserver }) => {
    const el = useRef(null);
    useEffect(() => {
        createObserver(el.current);
    }, []);

    return (
        <article ref={el} className={styles.article} id={id}>
            <h2>{title}</h2>
            <p className={styles.content}>{content}</p>
        </article>
    )
}

export default Article

Y voilá, eso es todo 🥳.

Ya tenemos creado y configurado nuestro observer, y ya está siendo usado por cada elemento a observar.

Espero que puedas aplicar lo aprendido en este artículo en tus proyecto.

¿Dudas? ¿Consultas?

Para comunicarte conmigo mandame un email o buscame en mis redes.

Eso es todo por ahora, nos vemos pronto 👋🏻.

¡wow! ¡tu monitor es muy grande!