1
Fork 0
- 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:
stz184 2021-03-31 04:58:28 +03:00
parent c40f3cc066
commit cdc13fce5c
6 changed files with 65 additions and 40 deletions

View File

@ -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])

View File

@ -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 (

View File

@ -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 (
<StyledPhoto
{...photoProps}
loading="lazy"
loaded={loaded ? 1 : 0}
onLoad={e => {
!e.target.dataset.src && setLoaded(true)
}}
onLoad={onLoad}
/>
)
}

View File

@ -27,11 +27,14 @@ export const ProtectedImage = ({ src, loading, ...props }) => {
props['data-src'] = getProtectedUrl(src)
}
if (isNativeLazyLoadSupported) {
props.loading = loading || 'eager'
}
return (
<img
key={src}
{...props}
loading={loading || 'eager'}
src={
loading === 'lazy' && !isNativeLazyLoadSupported
? placeholder

View File

@ -9,7 +9,7 @@ import useURLParameters from '../../hooks/useURLParameters'
import { FavoritesCheckbox } from '../AlbumFilter'
import useScrollPagination from '../../hooks/useScrollPagination'
import PaginateLoader from '../PaginateLoader'
import lazyLoad from '../../helpers/LazyLoad'
import LazyLoad from '../../helpers/LazyLoad'
const MY_TIMELINE_QUERY = gql`
query myTimeline($onlyFavorites: Boolean, $limit: Int, $offset: Int) {
@ -158,11 +158,13 @@ const TimelineGallery = () => {
}, [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
}

View File

@ -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()