No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Infinite Scrolling: limitando la carga de datos

12/20
Recursos

Aportes 19

Preguntas 1

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Para implementar el infine scrolling en todas las secciones, lo que hice fue lo siguiente:

  1. Crear (en main.js) una función llamada getPaginatedMovies que reciba todos los posibles parámetros y realice la lógica de chequear los condicionales y hacer el llamado a la API de la siguiente página de resultados:
async function getPaginatedMovies(
    endPoint, 
    {
        categoryId, 
        query
    } = {},
    ) {
    const { 
        scrollTop, 
        scrollHeight, 
        clientHeight 
    } = document.documentElement;

    const scrollAtBottom = (scrollTop + clientHeight) >= (scrollHeight - 15);
    const pageIsNotMax = page < maxPage;

    if (scrollAtBottom && pageIsNotMax) {
        page++;

        const { data } = await api(endPoint, {
            params: {
                page,
                with_genres: categoryId,
                query,
            },
        });
        const movies = data.results;

        printMoviePosters(movies, genericSection, true);
    } 
}
  1. (en main.js) Creé una función específica de paginación para cada sección, en las que que llamaba a la función global de paginación, y si era necesario volvía a sacar la info del hash para pasarla por parámetro:
function getPaginatedTrendingMovies() {
    getPaginatedMovies('/trending/movie/day');
}

function getPaginatedMoviesByCategory() {
    const [_, categoryData] = location.hash.split('=');
    const [categoryId] = categoryData.split('-');
    getPaginatedMovies('/discover/movie', {categoryId});
}

function getPaginatedMoviesBySearch() {
    const [_, undecodedQuery] = location.hash.split('=');
    const query = decodeURI(undecodedQuery);
    getPaginatedMovies('/search/movie', {undefined, query});
}
  1. (en navigation.js) La función de cada página asigna ahora a infiniteScroll su función propia de paginación:
function searchPage () {
   // función recortada
    infiniteScroll = getPaginatedMoviesBySearch;
}

function categoriesPage () {
   // función recortada
    infiniteScroll = getPaginatedMoviesByCategory;
}

function trendsPage () {
   // función recortada
    infiniteScroll = getPaginatedTrendingMovies;
}
  1. Luego me dí cuenta que si navegaba entre diferentes secciones, al compartir función page mantenía acumulado el último número en el que quedaba, por lo que si llegaba a la página 4 en trendings, al cargar una categoría y scrollear, no me cargaba la página 2 sino la 5, y así sucesivamente.
    Por lo tanto, (en navigation.js) reasigné a 1 la varible page antes de asignarle la función a infiniteScroll:
    page = 1;
    infiniteScroll = getPaginatedTrendingMovies;

De esta forma logré que las 3 secciones tengan infinite scrolling, intentando mantenerme DRY

*Cualquier error o correción, siempre súper bienvenido!

Yo resolví el problema obteniendo el id desde el hash

location.hash.split('=')[1].split('-')[0]

cree una funcion paga searMovies y una funcion para moviesByCategory y desde ahi antes de hacer la peticion tome del hash el id que necesite,

const [_, urlId, urlName] = location.hash.split("=");
async function getPageCategoryMovies() {
    const {scrollTop, clientHeight, scrollHeight} = document.documentElement;

    const scrollIsButton = (scrollTop + clientHeight) >= (scrollHeight - 25);

    const [_, urlId, urlName] = location.hash.split("=");
    const limitMaxPage = page < limitPage;

    if (scrollIsButton && limitMaxPage) {
        page++;
        const { data } = await api('discover/movie', {
            params : {
                with_genres : urlId,
                page,
            },
        });
    
        const movies = data.results;
    
        loadImage(genericSection, movies, {lazy: true, clear: false,});
    }

}

y lo mismo para search

En mi caso hice una función única para manejar todos los botones de carga infinita, de search, de categories y de trending.

/**
 * Maneja toda la lógica de los botones de carga.
 * 
 * @param {string} url La url del endpoint.
 * @param {string} currentPage La página actual que se recibe de la respuesta del endpoint.
 * @param {string} totalPages La cantidad total de páginas que se recibe del endpoint.
 * @param {string} container El contenedor principal o el section que está visible.
 * @param {object} extraParams Parámetros extra para mandar a la consulta.
 */
function loadMoreMoviesBtnHandler( url, currentPage, totalPages, container, extraParams ) {
    
    // Revisa si hay paginas para cargar.
    if (currentPage < totalPages) {

        // Crea el boton de carga y lo inserta unicamente en la seccion actual.
        const loadMoreBtn = document.createElement('button');

        loadMoreBtn.classList.add('loadMore');
        loadMoreBtn.innerText = 'Load More';
        loadMoreBtn.setAttribute('data-page', currentPage);

        // Agrega un observador al boton para disparar un evento de clic cuando sea visible.
        loadMoreMoviesObserver.observe(loadMoreBtn);

        container.appendChild(loadMoreBtn);

        // Agrega un event listener al boton.
        loadMoreBtn.addEventListener('click', async () => {
            let newCurrentPage = parseInt(loadMoreBtn.getAttribute('data-page')) + 1;

            const {data} = await api(url, {
                params: {
                    page: newCurrentPage,
                    ...extraParams
                }}
            );

            createMoviesMarkup(data.results, container, false);

            let lastNode = container.querySelector('.movie-container:last-child');
            lastNode.parentNode.insertBefore(loadMoreBtn, lastNode.nextSibling);
            loadMoreBtn.setAttribute('data-page', newCurrentPage);
        })
    }
    else {
        let loadMore = container.querySelectorAll('.loadMore');

        if ( loadMore.length ) {
            loadMore[0].remove();
        }
    }
}

También creé un Observer en vez de un eventListener de scroll (me pareció mas eficiente)

let loadMoreMoviesObserver = new IntersectionObserver((loadMoreObjetcs) => {
    loadMoreObjetcs.forEach(loadMoreButton => {
        
        if (loadMoreButton.isIntersecting) {
            loadMoreButton.target.click();
        }
    })
})

Y para llamar al handler lo hice así:

// Trending movies.
async function getTrendingMovies() {
    const endPoint = '/trending/movie/day';
    const {data} = await api(endPoint);

    const movies = data.results;

    createMoviesMarkup(movies, genericSection);
    loadMoreMoviesBtnHandler(endPoint, data.page, data.total_pages, genericSection);
}

// Search
async function getMoviesBySearch(query) {
    const endPoint = '/search/movie';
    const {data} = await api(endPoint, {
        params: {
            query,
        }
    });

    const movies = data.results;

    createMoviesMarkup(movies, genericSection);
    loadMoreMoviesBtnHandler(endPoint, data.page, data.total_pages, genericSection, {query});
}

// Categories
async function getMoviesByCategory(id) {
    const endPoint = '/discover/movie';
    const {data} = await api(endPoint, {
        params: {
            with_genres: id,
        }
    });

    const movies = data.results;

    createMoviesMarkup(movies, genericSection);
    loadMoreMoviesBtnHandler(endPoint, data.page, data.total_pages, genericSection, {with_genres: id});
}

Cuando intenté implementar el infinite scrolling por mi cuenta (de una forma distinta a la del Profesor Juan), también pensé que una buena forma de aplicarlo para todas las demás secciones sería mediante clousures para que así individualmente cada sección “recuerde” o “guarde” la página actual en la que se encuentra . Sin embargo… Al final ni siquiera me molesté en intentarlo debido a que en realidad (a pesar de que las conozco) no sé cómo crear ni usar las clousures, je je je 😅.
·
Por lo tanto al final terminé implementando todo de forma la cual dejaré por aquí~ Probablemente no sea la forma más bonita ni óptima, pero al menos funciona, je je 👉👈.
·
En mi solución, modifiqué las funciones getTrendingMovies, getMoviesBySearch y getMoviesByCategory para que recibieran un parámetro extra: el número de la página. De esta forma, cada vez que se llegue al final del scroll, simplemente se vuelve a llamar a las mismas funciones pero pasándoles la página que se supone deben cargar.

// Get Trending Movies.
async function getTrendingMovies(page = 1) {
	try {
		thereAreSomeRequestsInProcess = true;
		const isTheFirstLoad = (page === 1);
		
		if (isTheFirstLoad) {
			genericSection.innerHTML = "";
			showMoviesLoadingScreen(genericSection);
			
			const { data } = await api(`trending/movie/day?page=${page}`);
			const movies = data.results;
			currentLimitPagination = data.total_pages;
			createMovies(movies, genericSection, isTheFirstLoad);

			console.group("Respuestas del Servidor (GET Trending Movies)");
				console.log(data);
			console.groupEnd();
		} else {
			if (page <= currentLimitPagination) {
				showInfiniteScrollingMoviesLoadingScreen(genericSection);
				
				currentPagination = page;
				const { data } = await api(`trending/movie/day?page=${page}`);
				const movies = data.results;
				currentLimitPagination = data.total_pages;
				createMovies(movies, genericSection, isTheFirstLoad);
				if (page === currentLimitPagination) showInfiniteScrollingEndMessage(genericSection);
				
				console.group("Respuestas del Servidor (GET Trending Movies by Scrolling)");
					console.log(data);
				console.groupEnd();
			}
		}
		
		thereAreSomeRequestsInProcess = false;
		
	} catch (error) {
		console.group("%cError (GET Trending Movies)", consoleErrorMessageStyle);
			console.error(error);
		console.groupEnd();
		alert("Ocurrió un Error en el GET de las Películas en Tendencia.");
	}
}

·
¿Y quién se encarga de pasar el número de la página a cargar? Una función ubicada en el navigation.js donde también están las variables goblales que indican la página actual de la sección y el límite máximo de la misma.

// Infinite Scrolling

let currentPagination = 1; // Página Actual de la Sección
let currentLimitPagination = null; // Límite de la Paginación Actual
let thereAreSomeRequestsInProcess = false;

document.addEventListener("scroll", loadMoreMoviesByInfiniteScrolling);
function loadMoreMoviesByInfiniteScrolling() {
	const {scrollTop, scrollHeight, clientHeight} = document.documentElement;
	const endOfScrollReached = (scrollTop+clientHeight) >= (scrollHeight-100);
	if (endOfScrollReached && !thereAreSomeRequestsInProcess)  {
		thereAreSomeRequestsInProcess = true;
		
		if (location.hash === "#trends") {
			console.log(`Se llegó al final del scroll de la página ${currentPagination} en las tendencias.`);
			getTrendingMovies(currentPagination+1);
		}
		
		if (location.hash.startsWith("#search=")) {
			console.log(`Se llegó al final del scroll de la página ${currentPagination} en la búsqueda.`);
			const searchedTerm = location.hash.split("=")[1].trim();		
			getMoviesBySearch(searchedTerm, currentPagination+1);
		}
		
		if (location.hash.startsWith("#category=")) {
			console.log(`Se llegó al final del scroll de la página ${currentPagination} en la categoría.`);
			const categoryId = location.hash.split("=")[1].split("-")[0];
			getMoviesByCategory(categoryId, currentPagination+1);
		}
	}
}

Yo creé una función de paginación general que se puede usar con cualquier sección, y esta va a recibir los paramatros que necesite para crear el Infinite Scrolling. Para usarla simplemente se tiene que invocar dentro de la función inicial de cada sección.

//Esta funcion se usa para mostrar mas resultados de peliculas para las vistas de tendencias, categorias y búsqueda.
async function getPaginatedMovies({
    url,
    params,
    page,
    container
} = {})
{   
    const { data } = await api(url, {
        params:{
            ...params,
            page,
        }
    });  //se desestructura la respuesta de api para obtener los datos de una vez
    const movies = data.results; //movies es el objeto de peliculas según los datos iniciales. tiene una total de 20 elementos.
    console.log(movies);

    //se llama a la funcion createMovies para visualizar las peliculas según los datos iniciales
    createMovies({
        movies,
        container,
        movieModificator: '--small',
        clean: false,
    })
}

Ejemplo de implementación:

async function getMovieBySearch(query){
    let page = 1;  //Esta variable controla la páginación de la API.

    //se hace la solicitud GET con la instancia de AXIOS para traer el objeto de peliculas según la búsqueda del usuario.
    const { data } = await api(`search/movie`,{
        params:{
            'language': 'en-US',
            query,
        }
    });  //se desestructura la respuesta de api para obtener los datos de una vez
    const movies = data.results; //movies es el objeto de peliculas según la categoria. tiene una total de 20 elementos.
    
    //se llama a la funcion createMovies para visualizar las peliculas segun la búsqueda del usuario.
    createMovies({
        movies,
        container: genericMovieList,
        movieModificator: "--small",
    })

    //Cargar mas contenido

    //primero se valida que no exista una funcion infinite scrolling, y si si lo hay se elimina.
    if(infiniteScrolling){
        genericListSection.removeEventListener('scroll', infiniteScrolling);
        infiniteScrolling = undefined;
        console.log('test');
    }

//Se asigna la función especifíca de la sección al infinite scrolling.
    infiniteScrolling = () => {
        const isUserAtBottom = genericListSection.scrollTop + genericListSection.clientHeight >= genericListSection.scrollHeight - 5;

	//Se hace la validación si el usuario alcanzó el fondo de la pantalla
        if(isUserAtBottom){
            page++;   //Se suma uno a la página

	//Se invoca la función de paginación con los respectivos parametros de la sección.
            getPaginatedMovies({
            url: 'search/movie',
            params: {
                'language': 'en-US',
                query,
            },
            page,
            container: genericMovieList,
            })
        }
    }


    genericListSection.addEventListener('scroll', infiniteScrolling);
}

Yo implemente la solucion de obtener los parametros, ya sea el ID o el query, a traves de location.hash. Exactamente como lo hicimos en navigation.js.

No se si sea lo mas optimo pero funciona.

yo resolvi el problema asi:

infiniteScroll = () => {
    getPaginatedMoviesBySearch(realQuery)}```

A mi parecer es menos complicado de lo que parece, ya que, al cargar la 1ra pagina de nuestra seccion (seach o category) con location.hash podemos tomar ese id o ese query para hacer la solucitud de la 2da pagina y de las sucesivas. Espero haber sido claro y que pueda servirle a alguien!!

En mi solucion me sali de la tangente sin querer al elegir como primer funcion para implementar el infinite scrolling aquella que maneja las peliculas recomendadas dentro de la seccion movieDetail y me encontre con una serie de complicaciones a resolver sobre la marcha:
-La primera fue el hecho de que esta llamada a la API no debe utilizar la variable infiniteScrolling que definimos para los demas eventos de scroll debido a que trabaja con el scroll vertical de un contenedor y no con el scroll de la seccion visible en la que nos encontramos.
-La segunda fue que comprendi que en la clase previa nunca establecimos un reset en la variable “page”, por lo cual cada vez que la reutilizabamos en un ciclo de uso cotidiano de un usuario (entrar a una seccion, scrollear, salir, entrar a otra seccion, scrollear, etc) no arrancabamos desde la primer pagina disponible de la API, por lo que tuve que buscar el punto justo donde insertarle un reset sin que ello produzca problemas en el infinite scroll en si.
-Y por ultimo me parecia muy poco comodo el hecho de tener que arrastrar el scroll del contenedor “manualmente” para cargar mas peliculas debido a que eso provocaba muchas veces que la funcion se ejecutara varias veces consecutivas sin darme posibilidad de corregir la posicion del scroll y de esa forma evitar las multiples (si bien esto es algo a corregir en futuros updates), por lo cual termine implementado otro eventListener que se encargar de proveernos el scroll vertical con la rueda del mouse, tal cual lo hacemos en otras secciones.
A continuacion les dejo como quedaria el codigo:

Asi queda la navigation de la seccion movieDetails

function movieDetailsPage () {
    console.log("Movie!!")
    headerSection.classList.add('header-container--long');
    //headerSection.style.background = '';
    arrowBtn.classList.remove('inactive');
    arrowBtn.classList.add('header-arrow--white');
    headerTitle.classList.add('inactive');
    headerCategoryTitle.classList.add('inactive');
    searchForm.classList.add('inactive');

    trendingPreviewSection.classList.add('inactive');
    categoriesPreviewSection.classList.add('inactive');
    genericSection.classList.add('inactive');
    movieDetailSection.classList.remove('inactive');

    const [_, movieId] = location.hash.split('=') // ["#movie", 'movieID']
    getMovieDetailById(movieId);
    getRelatedMoviedById(movieId);
    //Aqui insertamos el reset de la pagina en esta funcion y en cualquier otra que haga uso del infiniteScroll
    page = 1;
}

Los eventos a escuchar dentro del contenedor

relatedMoviesContainer.addEventListener('scroll', getPaginatedRelatedMoviesbyId)
relatedMoviesContainer.addEventListener('wheel', scrollHorizontalContainer)

La funcion en si de llamada a la API

async function getPaginatedRelatedMoviesbyId() {
    const [_, movieId] = location.hash.split('=')
    const { scrollLeft, scrollWidth, clientWidth } = relatedMoviesContainer;

    const scrollIsEnd = (scrollLeft + clientWidth) >= (scrollWidth - 10);
    if (scrollIsEnd) {
        page++;
        const { data } = await api(urlSimilarMovies(movieId), {
            params: {
                page,
            }
    });
        const similarMovies = data.results;
        if (similarMovies) {
        movieRender(similarMovies, relatedMoviesContainer, {lazyLoad: true, clean: false});
        }
    }
}

Y por ultimo la funcion de scrollVertical haciendo uso del mouse

function scrollHorizontalContainer (event) {
	// Se puede modificar la velocidad corrigiendo el decimal
	relatedMoviesContainer.scrollLeft += event.deltaY * 0.50;
}

**Disclaimer
Hice uso del endpoint similarMovies debido a que testee muchas peliculas que no sean del todo populares (o son antiguas, etc) y el endpoint de Recommendations normalmente se encuentra vacio y por lo tanto no util para el uso de nuestra app

Yo lo resolví pero el código es un desastre 😂

function infiniteScroll() {
	const scrollCond =
		document.documentElement.scrollTop + document.documentElement.clientHeight >= document.documentElement.scrollHeight;
	if (scrollCond && location.hash.startsWith("#trends")) {
		getTrendingMovies({ pag: pag, clear: false });
		pag++;
	}

	if (scrollCond && location.hash.startsWith("#category=")) {
		const hashContent = location.hash;
		const idAndName = hashContent.slice(10);
		const idAndNameArray = idAndName.split("-");
		const clicked_id = idAndNameArray[0];
		const clicked_name = idAndNameArray[1].replace("%20", " ");

		getMoviesByCategory(clicked_id, clicked_name, pag, false);
		pag++;
	}

	if (scrollCond && location.hash.startsWith("#search=")) {
		const hashContent = location.hash;
		const [_, hashContentArray] = hashContent.split("=");
		const searchValue = hashContentArray.replaceAll("%20", " ");
		searchMovies(searchValue, { pag: pag, clear: false });
		pag++;
	}
}

Yo usé generators, me quedó así (está hecho rápido, todavía tengo que limpiar el código)

Navigation.js

function printMoviesByGenre() {
    genre.classList.add('active')
    const regex = /category=(\d+)-(.*)/
    const genreData = regex.exec(location.hash)
    const id = genreData[1]
    const name = genreData[2]

    container = moviesByGenre
    getMoviesByGenre({ id, name })
    const page = generator(1)

    document.onscroll = () =>
        getPaginatedMovies('discover/movie', { with_genres: id }, page)
}

Index.js

let maxPage
let container
let page = 1
function* generator(i) {
    while (true) {
        i++
        yield i
    }
}
//...
async function getPaginatedMovies(endpoint, params = {}, pageGenerator) {
    if (page > maxPage) return
    const { scrollTop, scrollHeight, clientHeight } = document.documentElement
    const scrollIsInBottom = scrollTop + clientHeight >= scrollHeight - 30

    if (scrollIsInBottom) {
        page = pageGenerator.next().value
        params.page = page
        const { data } = await api(endpoint, {
            params,
        })
        const movies = data.results
        createCards(movies, container, false)
    }
}

Este es mi aporte, lo realicé antes de ver la clase 11 pero lo del passive no lo sabía XD, así que lo agregué después. En esta implementación creo el escuchador de scroll y mando a llamar la función inifityScroll en la cual se detectará en que vista se está parado y si se llegó al máximo scroll posible según cada condicional.

/* PAGINATION */
let page = 1; //Para las listas de trending y popular
window.addEventListener("scroll", infinityScroll, {passive : false});
function infinityScroll(){
    //Se extrae el máximo scroll segúnla vista actual
    const maxScroll = document.documentElement.scrollHeight - window.innerHeight;
    if( (maxScroll === window.scrollY) && (location.hash === "#more-trends")){
        //Se aumenta la página
        page++;
        //Se manda a llamar la función de consulta
        trendingMovieViewMore();
        
    }
    if( (maxScroll === window.scrollY) && (location.hash === "#more-popular")){
        //Se aumenta la página
        page++;
        //Se manda a llamar la función de consulta
        popularMovieViewMore();
    }
    if( (maxScroll === window.scrollY) && (location.hash.startsWith("#category"))){
        //Se obtiene el id y nombre de categoría del hash usando split
        let [vista, categoryIdName] = location.hash.split("=");
        const [categoryId, categoryName] = categoryIdName.split("_");
        //Se aumenta la página
        page++;
        //Se manda a llamar la función de consulta
        getMovieByCategory({name: categoryName, id:categoryId});
    }
    if( (maxScroll === window.scrollY) && (location.hash.startsWith("#search"))){
        //Se obtiene el nombre de búsqueda en el hash usando split y join
        let [vista, searchName] = location.hash.split("=");
        let query = searchName.split("-");
        query = query.join(" ");
        //Se aumenta la página
        page++;
        //Se manda a llamar la función de consulta
        getMovieBySearch({query});
    }   
}

En las funciones de consulta como trendingviewmore() etc; cada una tiene el parámetro de page dentro de la consulta de axios. Adicional se agregan las siguientes
líneas para limpiar el contenedor de los posters de películas cuando es primera vez que se entra a dicha vista.

if(page <= 1)
   cardsContainer.innerHTML = ""; 

En navigation.js cada función de cambio de vista reiniciará la variable global “page” igualándola a 1, por ejemplo:

function trendingListPage(){
  
 //Se quitan las vistas que no se deben mostrar y se deja sólo la deseada
    category.classList.add("d-none");
    movieDetail.classList.add("d-none");
    movieDetail.classList.add("d-md-none");
    searching.classList.add("d-none");
    trending.classList.add("d-none");
    popular.classList.add("d-none");
    popularList.classList.add("d-none");
    searchBar.classList.add("d-none");
    trendingList.classList.remove("d-none");
    //Se reinicia la paginación
    page = 1;
    //Se manda a llamar las funciones generadoras de la información  
    trendingMovieViewMore();   
    window.scrollTo(0, 0);
}

aunque ese reinicio se puede hacer dentro de la función navigator también, ya que el objetivo es detectar un cambio de vista y reiniciar la paginación.

Como bonus cree un botón para hacer scroll hacia el top de las vistas ya que es dificil scrollear tanto luego de cargar mucha información y funciona con este código:

/* SCROLLTOP BUTTON */
const scrollTop = document.getElementById("scroll-button");
scrollTop.addEventListener("click", () =>{
    window.scrollTo(0, 0);
})

Mi solución para el Infinite Scrolling en páginas/secciones que reciben parámetros fue guardar estos valores en variables globales (como hicimos con maxPage) para luego poder acceder a ellos desde la función de Infinite Scrolling.

  1. Creé dos nuevas variables globales.
  • endpointInfiniteScroll: para guardar el endpoint y no tener que crear una función por cada sección
  • infiniteScrollParams: para guardar los parámetros como la categoría, búsqueda, etc.
// navigation.js
let page = 1;
let maxPage;
let infiniteScroll;
let endpointInfiniteScroll;
let infiniteScrollParams = {
  params: {
    page: 1,
  },
};
  1. Luego, desde la primera petición en la función GET asigné el endpoint y los parámetros a las variables globales ya inicializadas.
// main.js 
async function getMoviesByCategory(id) {
  endpointInfiniteScroll = "/discover/movie?with-genres=";
  infiniteScrollParams = {
    params: {
      with_genres: id,
    },
  }

  const { data } = await api(endpointInfiniteScroll , infiniteScrollParams);

  maxPage = data.total_pages;

  createMovies(data.results, genericListMoviesPreview);

}
  1. Después, en la función del Infinite Scrolling usé las variables globales para realizar la petición según los parámetros y endpoint que se usaron para el primer GET.
//main.js 
async function getInfiniteMoviesList() {
  const { scrollTop, scrollHeight, clientHeight } = document.documentElement;

  const scrollIsBottom = (scrollTop + clientHeight >= scrollHeight - 15);

  const pageIsNotMax = (page < maxPage);

  if (scrollIsBottom && pageIsNotMax) {

    page++;

    infiniteScrollParams.params.page = page;
    
    const { data } = await api(endpointInfiniteScroll, infiniteScrollParams);

    createMovies(data.results, genericListMoviesPreview, {
      cleanSection: false,
    });

  }
  1. En la función “navigator” para cada vez que cambiemos de sección establecí un valor inicial por defecto para estas variables, así, en secciones que no se necesiten estos valores no causen errores o se vayan acumulando.
//navigation.js
function navigator() {

  if(infiniteScroll){
    window.removeEventListener('scroll', infiniteScroll, {passive: false});
    infiniteScroll = undefined;
    page = 1;
    maxPage = 0;
    endpointInfiniteScroll = undefined;
    infiniteScrollParams = {
      params: {
        page: 1,
      },
    };
  }

}
  1. Por último, en la página o sección que queramos usar el Infinite Scrolling asignamos su función a la variable global de “infiniteScroll”.
//navigation.js

function categoriesPage() {
// función recortada
  infiniteScroll = getInfiniteMoviesList;
}

La solución es crear esas funciones reutiilizables para que pueda usarse con págiina 1 hasta la página n, también desde una función promesa podemos retornar un valor específico y cuando la usemos podemos catcharla con .then y allí hacer lo que queramos

const getTrendingMovies = async (page = 1) => {
    const dataUtil = {};
    try {
        const { status, data } = await api.get(URL_TRENDING_RES('movie', 'day'), {
            params: { page },
        });
        ...
        dataUtil.total_pages = data.total_pages;
    } catch (error) {
        ...
    }
    return dataUtil;
};

Para catcharla

const trendsPage = () => {
    ...
    getTrendingMovies().then((data) => {
        if (data.total_pages === 1) removeInfiniteScroll();
    });
    // Scroll infinito
    infiniteScroll = () => {
        if (scrollIsOnThreshold()) {
            getTrendingMovies(page).then((data) => {
                if (page >= data.total_pages) removeInfiniteScroll();
            });
        }
    };
};

.
.
Mi propuesta, commit actual: Click aquí

Yo lo resolvi de esta manera Cree 2 variables por fuera de toda funcion ![](https://static.platzi.com/media/user_upload/image-921e7cd5-f52e-4f56-a271-af215b9b9cfb.jpg) hice un refactor de pues estaba repitiendo codigo por cada peticion que se hacia ![](https://static.platzi.com/media/user_upload/image-e80ff5bc-6317-4352-b678-244da4422ae9.jpg) si ya se estan cargando datos, no se hacen mas peticiones, por eso coloco que si isLoading es true no genere otro llamado pero seguido, isLoading pasa de nuevo a true para hacer una nueva peticion luego de cada forEach, page se incrementa a 1 para pasar a la siguiente pagina y isLoading pasa a false para que no este haciendo peticiones cada vez que se genere el scroll en la funcion que cree para el scroll infinito ![](https://static.platzi.com/media/user_upload/image-88fae279-a233-4394-9494-db06dfe6890c.jpg) pongo parametros con un valor null para que sea llamado segun la funcion que se ejecute en cada vista del navigation y hago la validacion para generar el scroll Finalmente hago el llamado en cada funcion por cada vista ![](https://static.platzi.com/media/user_upload/image-f147b771-9d84-4ff1-8980-6d291aad8c6e.jpg) ![](https://static.platzi.com/media/user_upload/image-3a7dd048-ad59-4feb-80bc-1c4ec273864a.jpg)
Hola platzinautas 😁, la solución que encontre fue la siguiente: 1ro - definí una variable global infiniteScrollParams: `let ``infiniteScrollParams;` 2do - luego la agregué en la validación de la funcion Navigator() para poder resetearla al cambiar de página: `if ``(infiniteScroll) {` ` window.removeEventListener('scroll', infiniteScroll, {passive: ``false``});` ` infiniteScroll = ``undefined``;` ` infiniteScrollParams = ``undefined``;` ` page = 1;` `}` 3ro - al final de cada una de mis funciones de navegación le asigne el valor de la función necesaria a la variable infiniteScroll, y de necesitar parametros se los asigno a la variable global infiniteScrollParams: `function ``searchPage() {` `...` ` getMoviesBySearch(searchValue)` ` infiniteScrollParams = {searchValue: searchValue}` ` infiniteScroll = getPaginatedMoviesBySearch;` `}` 4to - luego en las funciones que necesitan parametros necesite hacer un destructuring para obtener el parametro necesario: `async function ``getPaginatedMoviesBySearch(params) {` ` ``const ``{searchValue} = params` ` ...` `const ``{data} = ``await ``API('/search/movie', {` ` params: {` ` query: searchValue,` ` page: page` ` },` `})}` 5to - por ultimo agregué el evento: `window.addEventListener('scroll', handleInfiniteScroll, ``false``)` `function ``handleInfiniteScroll() {` ` ``try ``{` ` infiniteScroll(infiniteScrollParams)` ` } ``catch ``(error) {` ` }` `}` Me tomo un buen tiempito , espero les sirva mi solucion.🥵

Para hacer a la función getPaginatedTrendingMovies un poco más general, use el evento ‘onsroll’, en vez de la variable infiniteScroll, asi podemos enviar parametros, que en este caso sería la url de la seccion en la se mande a llamar a la función.

  • En trendsPage:
document.onscroll = () => getPaginatedMovies('trending/movie/day')
  • En categoriesPage:
document.onscroll = () => getPaginatedMovies('discover/movie')
  • Funcion getPginatedMovies:
async function getPaginatedMovies(url) {
    const { scrollTop, scrollHeight, clientHeight } = document.documentElement  
    const scrollBottom = (scrollTop + clientHeight) >= (scrollHeight - 15)
    const pageIsNotIMax = page < maxPage

    if (scrollBottom && pageIsNotIMax) {
        page++
        const {data} = await api(url, {
            params: {
                page
            }
        }); 
        const movies = data.results;
        console.log(data);
        renderMovies(movies, genericListSection, {lazyLoad : true, clean: false})
    }
}

Para no tener problemas con los parámetros, lo que hice es que esas variables que recibe se hagan globales…por ejemplo en el caso de searchMovies:

De tal manera que así se puede aplicar la lógica del scroll infinito al igual que en trendingMovies