Oscar Fuentes Esteves
Juan David Reyes
Oscar Fuentes Esteves
Guadalupe Monge Barale
Gilbert Ardila
Daniel Fernando Meneses Jaimes
Juan Castro
ProfesorLuis Angel Olivárez Estrada
Magalí Acevedo
John Jairo Rodriguez Rivas
Bramucci Candela
Diego fernando Vega Lopez
Lautaro Strappazzon
Ivan David Martinez Baron
Kevin Daniel Hincapie Lumbaque
Eliezer Hernandez
Luis Paredes
Walter Llanos
Jason Francisco Macas Mora
Juan Castro
ProfesorAbigail Nahomi Chávez Lozano
Axel Enrique Galeed Gutierrez
Rubén Cuello
Juan Castro
ProfesorSebastian Giraldo
Fabián Nava Pereda
Sebastián Luna Rodríguez
Raycris Maldonado
Jesús Manuel Návar Aragón
Deben quitar el async de la función "invocadora" del closure, si lo dejan, cosas raras pasan y se pueden pasar dos horas debuggeando.
Odio y adoro este tipo de clases.
Cual es la funcion invocadora?
En mi casi la función invocadora es pgetPaginatedMoviesBySearch, en el video en el minuto 6:30 quita el async de esa función, a mi se me fue quitarlo.
Resumen de Closure . Un closure es la combinación entre una función y el ámbito léxico en el que esta fue declarada. Con esto, la función recuerda el ámbito en el que se creó
Recuerda la asignación anterior . En esta clase se explica esto y se muestra un ejemplo
Ahora hay que implementar un botón para volver al inicio de la página cuando estemos haciendo el scroll infinito
En la navegación, cada vez que se añade el evento del scroll con la funcion de paginación a ejecutar, se debería de devolver la page = 1, para limpiar la variable de las posibles navegaciones que se realicen en otras secciones, con esto aseguramos que el endpoint que se vaya a ejecutar cargue de la página 1++.
Muy buena observación
hola ¿Cómo lo implementaste en tu código?
Yo como no tengo seccion de tendencias hacia abajo lo implemente en search primero y lo habia puesto:
infiniteScroll = ()=> getPaginatedMovies(keyword) ;
same
yep pensé lo mismo
Yo lo resolvi de la siguiente forma:
infiniteScroll = () => { getPaginatedCategories(categoryId)};
Excelente clase! Que gran uso de closures!
Continuando con mi aporte de la clase pasada, y mejorándolo gracias a lo aprendido en esta clase, ya no es necesario volver a conseguir el query ni el categoryId del hash como lo hacía:
main.js
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}); }
Sino que directamente se los paso como parámetro en navigation.js:
infiniteScroll = getPaginatedMoviesByCategory(categoryId); infiniteScroll = getPaginatedMoviesBySearch(query);
Y retorno una función que ejecuta la función global de paginación:
main.js
function getPaginatedMoviesByCategory(categoryId) { return function () { getPaginatedMovies('/discover/movie', {categoryId}); } } function getPaginatedMoviesBySearch(query) { return function () { getPaginatedMovies('/search/movie', {undefined, query}); } } 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); } else if (scrollAtBottom && !pageIsNotMax) { maxPageReached.classList.remove('inactive'); }; }
Es la primera vez que veo útil los closures en js.
Hola, tengo un par de apuntes que yo considero importantes para compartir:
Los closures son una característica poderosa de JavaScript que permite crear funciones que pueden recordar el estado de las variables en el momento en que se crearon, lo que permite una mayor flexibilidad y funcionalidad en el código.
Por otro lado, el currying es una técnica de programación funcional que consiste en transformar una función con varios argumentos en una serie de funciones que toman uno o más argumentos. La función curried devuelve otra función que toma el siguiente argumento, y así sucesivamente, hasta que se han proporcionado todos los argumentos necesarios.
En ambas técnicas, se utilizan funciones anidadas en JavaScript para lograr su funcionalidad. En el caso de los closures, la función interna (anidada) se utiliza para acceder a las variables de su ámbito externo, mientras que en el currying, la función interna (anidada) se utiliza para devolver una nueva función que toma un argumento adicional.
Falto resetear la variable page Puede que faltara asignar a la variable global "page" el valor 1, debido al pasar a otra sección(vista de películas por categorías, vista de películas por busqueda) si el usuario ha navegado previamente en otras secciones que modifican a page como la de trending movies probablemente el valor de page tendrá un valor mayor a 1 y ese valor será utilizado para solicitar películas en las demás secciones que lo requieran al realizar un scroll.
yo implemente esta solucion y me funciono bien hasta donde he probado
infiniteScroll = () => { getPaginatedMoviesBySearch(realQuery)}
FUN FACT: "Drama" es la categoría con mas películas. Tiene +9,400 pages x 20movies = 188,000movies
Quería comentar, que agregué un patrón Throttle para evitar la repetición innecesaria de peticiones, durante el scrolling. Pueden consultar más del tema aquí. Thottle y Debounce nos ayudan a gestionar mejor la ejecución de callbacks, y peticiones, en situaciones propensas a cuellos de botella. Adjunto el repositorio de mi código.
El infinite scrolling de la vista de películas de categorías me quedó así:
const throttleGetMoviesByCategory = throttle(getMoviesByCategory, 250); const scrollingMoviesByCategory = async () => { const { page } = categoriesHistory; if(scrollBottomReached()){ await throttleGetMoviesByCategory({...categoriesHistory, page: page + 1 }) } }
En categoriesHistory solo alamcené las variables de navegación y estado de la vista de películas por categoría:
categoriesHistory = { categoryId, categoryName, page, total_pages, };
La función throttle es:
const throttle = (cb, delay = 500) => { let waiting = false; return async (...args) => { if (waiting) return; waiting = true; try { await cb(...args); }catch(err){ console.log("Error: ", err); } await sleep(delay); waiting = false; } }
y sleep, una función auxiliar para aplicar delay al código, que en este caso se usa para darle margen a la petición (promesa) de completarse, sin que concurrentemente, se inicien nuevas peticiones al mismo URL, en este caso páginación. (Misma página / datos redundantes).
const sleep = async (ms = 1000) => { return new Promise(resolve => setTimeout(resolve, ms)); }
:clap:
En mi caso habia inhabilitado a 'infititeScroll', asi que use el doble paréntesis que dijo el profesor si no se trabajara con el addEventListener, para invocar a la función de paginación:
Les comparto mi solución para el scroll infinito para cada una de las páginas. :D Repositorio de GitHub
export const getPaginatedMovies = async () => { const { scrollTop: SCROLL_TOP, scrollHeight: SCROLL_HEIGHT, clientHeight: CLIENT_HEIGHT } = document.documentElement; const IS_SCROLL_BOTTOM = (SCROLL_TOP + CLIENT_HEIGHT) >= (SCROLL_HEIGHT - 15); const IS_NOT_HOME = location.hash !== '#home'; if (IS_SCROLL_BOTTOM && IS_NOT_HOME) { const HASHES_ROUTES = { '#trends' : 'trending/movie/day', '#category' : 'discover/movie', '#search' : 'search/movie' }; const DATA_OF_HASH = location.hash.split('='); const HASH_NAME = DATA_OF_HASH[0]; const EXTRA_INFO = DATA_OF_HASH[1]; const ROUT = HASHES_ROUTES[HASH_NAME]; const params = { page : pageMovies(), with_genres : '', query : '' }; if (HASH_NAME === '#category') { const [ID_CATEGORY] = EXTRA_INFO.split('-'); params.with_genres = ID_CATEGORY; } if (HASH_NAME === '#search') { const QUERY_SEARCH = EXTRA_INFO; params.query = QUERY_SEARCH; } const RESPONSE = await api(ROUT, { params }); const DATA = RESPONSE.data; const MOVIES = DATA.results; const IS_MAX_PAGE = DATA.page > DATA.total_pages; if (IS_MAX_PAGE) return; const IS_CAROUSEL = false; insertMovies(MOVIES, GENERIC_LIST_CONTAINER, IS_CAROUSEL, { clean: false }); } }; export const currentPageMoviesUpdate = () => { let pageMovies = 1; return function (refresh = false) { pageMovies = (refresh) ? 1 : pageMovies + 1; return pageMovies; }; };
Para los que no son fan de los closures les comparto mi solución al problema "como obtener el query":
Espero les sea útil!
Hmmm... Baja un poco la reusabilidad de esa función. Pero para efectos prácticos de que solo la usaremos en este proyecto tiene todo el sentido del mundo. :clap:
no estaria mal usar el decordURI por que se estaria escapando un caracter con el que tambien suele concatenar espacios en blanco
Este había sido como lo resolví
// Navigation.js getPaginatedMoviesBySearch.query = query getPaginatedMoviesBySearch = getPaginatedMoviesBySearch.bind(getPaginatedMoviesBySearch) infiniteScroll = getPaginatedMoviesBySearch
// Function getPaginatedMoviesBySearch query = this.query
Yo cuando hizo return de una función: *Quedé
Hola, si tienen el error de que no les funciona la funcion de, porque como copiamos la misma funcion de getMoviesBySearch y esta viene con un async, debemos de quitarselo, aqui un ejemplo mas claro
ANTES NO FUNCIONANDO
const getPaginatedMoviesBySearch = async (query) => { return async function () { const { scrollTop, scrollHeight, clientHeight } = document.documentElement; const scrollIsBottom = scrollTop + clientHeight >= scrollHeight - 15; const pageIsNotMax = page < maxPage; if (scrollIsBottom && pageIsNotMax) { page++; const { data } = await api(`search/movie`, { params: { query, page, }, }); const movies = data.results; createMovies(movies, genericSection, { lazyLoad: true, clean: false }); } }; };
FUNCIONANDO La unica diferencia es la palabra reservada async que causa que no funcione.
const getPaginatedMoviesBySearch = (query) => { return async function () { const { scrollTop, scrollHeight, clientHeight } = document.documentElement; const scrollIsBottom = scrollTop + clientHeight >= scrollHeight - 15; const pageIsNotMax = page < maxPage; if (scrollIsBottom && pageIsNotMax) { page++; const { data } = await api(`search/movie`, { params: { query, page, }, }); const movies = data.results; createMovies(movies, genericSection, { lazyLoad: true, clean: false }); } }; };
Agregue el Infinite Scrolling a las películas relacionadas
function getPaginatedRelatedMovies(id) { return async function() { const scrollWidthScreen = document.documentElement.scrollWidth; const { scrollLeft, scrollWidth } = relatedMoviesContainer; const scrollIsEnd = ((scrollWidth + scrollLeft) >= (scrollWidthScreen - 100)); const pageIsNotMax = page < maxPage; if (scrollIsEnd && pageIsNotMax) { page++; const { data } = await api(`movie/${id}/similar`, { params: { page } }); const movie = data.results; printMovies(movie, relatedMoviesContainer, {clean: false, lazyLoad: true}) } } }
Agregué el evento listener al contenedor de películas relacionadas
relatedMoviesContainer.addEventListener("scroll", infiniteScroll, false);
El contenedor me empezó a dar problemas así que le agrege el max-height
.relatedMovies-scrollContainer { position: absolute; overflow-x: scroll; overflow-y: hidden; white-space: nowrap; width: calc(100vw - 24px); padding-bottom: 16px; max-height: 187px; }