- 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 <img /> rendered by <ProtectedImage/> component
This commit is contained in:
parent
c40f3cc066
commit
cdc13fce5c
|
@ -7,7 +7,7 @@ import Layout from '../../Layout'
|
||||||
import useURLParameters from '../../hooks/useURLParameters'
|
import useURLParameters from '../../hooks/useURLParameters'
|
||||||
import useScrollPagination from '../../hooks/useScrollPagination'
|
import useScrollPagination from '../../hooks/useScrollPagination'
|
||||||
import PaginateLoader from '../../components/PaginateLoader'
|
import PaginateLoader from '../../components/PaginateLoader'
|
||||||
import lazyLoad from '../../helpers/LazyLoad'
|
import LazyLoad from '../../helpers/LazyLoad'
|
||||||
|
|
||||||
const albumQuery = gql`
|
const albumQuery = gql`
|
||||||
query albumQuery(
|
query albumQuery(
|
||||||
|
@ -126,12 +126,13 @@ function AlbumPage({ match }) {
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
lazyLoad(document.querySelectorAll('img[data-src]'))
|
LazyLoad.loadImages(document.querySelectorAll('img[data-src]'))
|
||||||
|
return () => LazyLoad.disconnect()
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
lazyLoad(document.querySelectorAll('img[data-src]'))
|
LazyLoad.loadImages(document.querySelectorAll('img[data-src]'))
|
||||||
}
|
}
|
||||||
}, [finishedLoadingMore, onlyFavorites, loading])
|
}, [finishedLoadingMore, onlyFavorites, loading])
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, { useEffect } from 'react'
|
||||||
import AlbumBoxes from '../../components/albumGallery/AlbumBoxes'
|
import AlbumBoxes from '../../components/albumGallery/AlbumBoxes'
|
||||||
import Layout from '../../Layout'
|
import Layout from '../../Layout'
|
||||||
import { useQuery, gql } from '@apollo/client'
|
import { useQuery, gql } from '@apollo/client'
|
||||||
import lazyLoad from '../../helpers/LazyLoad'
|
import LazyLoad from '../../helpers/LazyLoad'
|
||||||
|
|
||||||
const getAlbumsQuery = gql`
|
const getAlbumsQuery = gql`
|
||||||
query getMyAlbums {
|
query getMyAlbums {
|
||||||
|
@ -22,9 +22,11 @@ const AlbumsPage = () => {
|
||||||
const { loading, error, data } = useQuery(getAlbumsQuery)
|
const { loading, error, data } = useQuery(getAlbumsQuery)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading) {
|
return () => LazyLoad.disconnect()
|
||||||
lazyLoad(document.querySelectorAll('img[data-src]'))
|
}, [])
|
||||||
}
|
|
||||||
|
useEffect(() => {
|
||||||
|
!loading && LazyLoad.loadImages(document.querySelectorAll('img[data-src]'))
|
||||||
}, [loading])
|
}, [loading])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useCallback, useState } from 'react'
|
||||||
import { useMutation, gql } from '@apollo/client'
|
import { useMutation, gql } from '@apollo/client'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
@ -36,15 +36,16 @@ const StyledPhoto = styled(ProtectedImage)`
|
||||||
|
|
||||||
const LazyPhoto = photoProps => {
|
const LazyPhoto = photoProps => {
|
||||||
const [loaded, setLoaded] = useState(false)
|
const [loaded, setLoaded] = useState(false)
|
||||||
|
const onLoad = useCallback(e => {
|
||||||
|
!e.target.dataset.src && setLoaded(true)
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledPhoto
|
<StyledPhoto
|
||||||
{...photoProps}
|
{...photoProps}
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
loaded={loaded ? 1 : 0}
|
loaded={loaded ? 1 : 0}
|
||||||
onLoad={e => {
|
onLoad={onLoad}
|
||||||
!e.target.dataset.src && setLoaded(true)
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,11 +27,14 @@ export const ProtectedImage = ({ src, loading, ...props }) => {
|
||||||
props['data-src'] = getProtectedUrl(src)
|
props['data-src'] = getProtectedUrl(src)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isNativeLazyLoadSupported) {
|
||||||
|
props.loading = loading || 'eager'
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
key={src}
|
key={src}
|
||||||
{...props}
|
{...props}
|
||||||
loading={loading || 'eager'}
|
|
||||||
src={
|
src={
|
||||||
loading === 'lazy' && !isNativeLazyLoadSupported
|
loading === 'lazy' && !isNativeLazyLoadSupported
|
||||||
? placeholder
|
? placeholder
|
||||||
|
|
|
@ -9,7 +9,7 @@ import useURLParameters from '../../hooks/useURLParameters'
|
||||||
import { FavoritesCheckbox } from '../AlbumFilter'
|
import { FavoritesCheckbox } from '../AlbumFilter'
|
||||||
import useScrollPagination from '../../hooks/useScrollPagination'
|
import useScrollPagination from '../../hooks/useScrollPagination'
|
||||||
import PaginateLoader from '../PaginateLoader'
|
import PaginateLoader from '../PaginateLoader'
|
||||||
import lazyLoad from '../../helpers/LazyLoad'
|
import LazyLoad from '../../helpers/LazyLoad'
|
||||||
|
|
||||||
const MY_TIMELINE_QUERY = gql`
|
const MY_TIMELINE_QUERY = gql`
|
||||||
query myTimeline($onlyFavorites: Boolean, $limit: Int, $offset: Int) {
|
query myTimeline($onlyFavorites: Boolean, $limit: Int, $offset: Int) {
|
||||||
|
@ -158,11 +158,13 @@ const TimelineGallery = () => {
|
||||||
}, [onlyFavorites])
|
}, [onlyFavorites])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading) {
|
!loading && LazyLoad.loadImages(document.querySelectorAll('img[data-src]'))
|
||||||
lazyLoad(document.querySelectorAll('img[data-src]'))
|
|
||||||
}
|
|
||||||
}, [finishedLoadingMore, onlyFavorites, loading])
|
}, [finishedLoadingMore, onlyFavorites, loading])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => LazyLoad.disconnect()
|
||||||
|
}, [])
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return error
|
return error
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,48 @@
|
||||||
const lazyLoad = elements => {
|
class LazyLoad {
|
||||||
function setSrcAttribute(element) {
|
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')) {
|
if (element.hasAttribute('data-src')) {
|
||||||
const src = element.getAttribute('data-src')
|
const src = element.getAttribute('data-src')
|
||||||
element.removeAttribute('data-src')
|
element.removeAttribute('data-src')
|
||||||
element.setAttribute('src', 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()
|
||||||
|
|
Loading…
Reference in New Issue