From cdc13fce5c3d80b987a37142cfce829a507cf411 Mon Sep 17 00:00:00 2001 From: stz184 Date: Wed, 31 Mar 2021 04:58:28 +0300 Subject: [PATCH] Fixes photoview/issues#189 - LazyLoad helper is now a singleton class reusing the same IntersectionObserver instance for performance reasons - IntersectionObserver is now disconnected when the component is unmounted for performance reasons - StyledPhoto is now using useCallback to avoid unnecessary rerenderings - Fixed issue setting loading attribute to the rendered by component --- ui/src/Pages/AlbumPage/AlbumPage.js | 7 +- ui/src/Pages/AllAlbumsPage/AlbumsPage.js | 10 +-- .../components/photoGallery/MediaThumbnail.js | 9 +-- .../components/photoGallery/ProtectedMedia.js | 5 +- .../timelineGallery/TimelineGallery.js | 10 +-- ui/src/helpers/LazyLoad.js | 64 ++++++++++++------- 6 files changed, 65 insertions(+), 40 deletions(-) diff --git a/ui/src/Pages/AlbumPage/AlbumPage.js b/ui/src/Pages/AlbumPage/AlbumPage.js index ad573afb..7ea9378b 100644 --- a/ui/src/Pages/AlbumPage/AlbumPage.js +++ b/ui/src/Pages/AlbumPage/AlbumPage.js @@ -7,7 +7,7 @@ import Layout from '../../Layout' import useURLParameters from '../../hooks/useURLParameters' import useScrollPagination from '../../hooks/useScrollPagination' import PaginateLoader from '../../components/PaginateLoader' -import lazyLoad from '../../helpers/LazyLoad' +import LazyLoad from '../../helpers/LazyLoad' const albumQuery = gql` query albumQuery( @@ -126,12 +126,13 @@ function AlbumPage({ match }) { ) useEffect(() => { - lazyLoad(document.querySelectorAll('img[data-src]')) + LazyLoad.loadImages(document.querySelectorAll('img[data-src]')) + return () => LazyLoad.disconnect() }) useEffect(() => { if (!loading) { - lazyLoad(document.querySelectorAll('img[data-src]')) + LazyLoad.loadImages(document.querySelectorAll('img[data-src]')) } }, [finishedLoadingMore, onlyFavorites, loading]) diff --git a/ui/src/Pages/AllAlbumsPage/AlbumsPage.js b/ui/src/Pages/AllAlbumsPage/AlbumsPage.js index 6760f2e1..0751e06a 100644 --- a/ui/src/Pages/AllAlbumsPage/AlbumsPage.js +++ b/ui/src/Pages/AllAlbumsPage/AlbumsPage.js @@ -2,7 +2,7 @@ import React, { useEffect } from 'react' import AlbumBoxes from '../../components/albumGallery/AlbumBoxes' import Layout from '../../Layout' import { useQuery, gql } from '@apollo/client' -import lazyLoad from '../../helpers/LazyLoad' +import LazyLoad from '../../helpers/LazyLoad' const getAlbumsQuery = gql` query getMyAlbums { @@ -22,9 +22,11 @@ const AlbumsPage = () => { const { loading, error, data } = useQuery(getAlbumsQuery) useEffect(() => { - if (!loading) { - lazyLoad(document.querySelectorAll('img[data-src]')) - } + return () => LazyLoad.disconnect() + }, []) + + useEffect(() => { + !loading && LazyLoad.loadImages(document.querySelectorAll('img[data-src]')) }, [loading]) return ( diff --git a/ui/src/components/photoGallery/MediaThumbnail.js b/ui/src/components/photoGallery/MediaThumbnail.js index 354c16a7..be628584 100644 --- a/ui/src/components/photoGallery/MediaThumbnail.js +++ b/ui/src/components/photoGallery/MediaThumbnail.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useCallback, useState } from 'react' import { useMutation, gql } from '@apollo/client' import PropTypes from 'prop-types' import styled from 'styled-components' @@ -36,15 +36,16 @@ const StyledPhoto = styled(ProtectedImage)` const LazyPhoto = photoProps => { const [loaded, setLoaded] = useState(false) + const onLoad = useCallback(e => { + !e.target.dataset.src && setLoaded(true) + }, []) return ( { - !e.target.dataset.src && setLoaded(true) - }} + onLoad={onLoad} /> ) } diff --git a/ui/src/components/photoGallery/ProtectedMedia.js b/ui/src/components/photoGallery/ProtectedMedia.js index a13f15b2..45d2b34a 100644 --- a/ui/src/components/photoGallery/ProtectedMedia.js +++ b/ui/src/components/photoGallery/ProtectedMedia.js @@ -27,11 +27,14 @@ export const ProtectedImage = ({ src, loading, ...props }) => { props['data-src'] = getProtectedUrl(src) } + if (isNativeLazyLoadSupported) { + props.loading = loading || 'eager' + } + return ( { }, [onlyFavorites]) useEffect(() => { - if (!loading) { - lazyLoad(document.querySelectorAll('img[data-src]')) - } + !loading && LazyLoad.loadImages(document.querySelectorAll('img[data-src]')) }, [finishedLoadingMore, onlyFavorites, loading]) + useEffect(() => { + return () => LazyLoad.disconnect() + }, []) + if (error) { return error } diff --git a/ui/src/helpers/LazyLoad.js b/ui/src/helpers/LazyLoad.js index 1e19a40a..6892bf2b 100644 --- a/ui/src/helpers/LazyLoad.js +++ b/ui/src/helpers/LazyLoad.js @@ -1,32 +1,48 @@ -const lazyLoad = elements => { - function setSrcAttribute(element) { +class LazyLoad { + constructor() { + this.observe = this.observe.bind(this) + this.loadImages = this.loadImages.bind(this) + this.disconnect = this.disconnect.bind(this) + this.observer = null + } + + observe(images) { + if (!this.observer) { + this.observer = new IntersectionObserver(entries => { + entries.forEach(entry => { + if (entry.isIntersecting || entry.intersectionRatio > 0) { + const element = entry.target + this.setSrcAttribute(element) + this.observer.unobserve(element) + } + }) + }) + } + Array.from(images).forEach(image => this.observer.observe(image)) + } + + loadImages(elements) { + const images = Array.from(elements) + if (images.length) { + if ('IntersectionObserver' in window) { + this.observe(images) + } else { + images.forEach(image => this.setSrcAttribute(image)) + } + } + } + + disconnect() { + this.observer && this.observer.disconnect() + } + + setSrcAttribute(element) { if (element.hasAttribute('data-src')) { const src = element.getAttribute('data-src') element.removeAttribute('data-src') element.setAttribute('src', src) } } - - function loadImage(observer, element) { - setSrcAttribute(element) - observer.unobserve(element) - } - - const images = Array.from(elements) - if (images.length) { - if (!('IntersectionObserver' in window)) { - images.forEach(image => setSrcAttribute(image)) - } else { - const observer = new IntersectionObserver(entries => { - entries.forEach(entry => { - if (entry.isIntersecting || entry.intersectionRatio > 0) { - loadImage(observer, entry.target) - } - }) - }) - images.forEach(image => observer.observe(image)) - } - } } -export default lazyLoad +export default new LazyLoad()