Cómo usar Intersection Observer API en Next.js
Tags: spanish
Published: 2022-07-18 13:34:51

¡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](https://intersection-observer.lucho.codes/) y al [repositorio completo del proyecto en GitHub](https://github.com/luchoPenafiel/intersection-observer).

## 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`
```react
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`
```react
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í:

```react
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í:

```react
// 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:

```react
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.

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