From a32ae737d531e3790d63d3364e711c36bc84cc4b Mon Sep 17 00:00:00 2001 From: dogyeong Date: Sat, 31 May 2025 21:28:40 +0900 Subject: [PATCH 1/3] refactor: replace react-lazy-images with native lazy loading --- .../src/components/lazy-image.tsx | 108 +++++++++--------- 1 file changed, 53 insertions(+), 55 deletions(-) diff --git a/packages/react-notion-x/src/components/lazy-image.tsx b/packages/react-notion-x/src/components/lazy-image.tsx index 3e45cdad..eea26840 100644 --- a/packages/react-notion-x/src/components/lazy-image.tsx +++ b/packages/react-notion-x/src/components/lazy-image.tsx @@ -1,6 +1,5 @@ import { normalizeUrl } from 'notion-utils' import * as React from 'react' -import { ImageState, LazyImageFull } from 'react-lazy-images' import { useNotionContext } from '../context' import { cs } from '../utils' @@ -59,6 +58,11 @@ export function LazyImage({ [zoomable, attachZoom] ) + const [isLazyLoaded, setIsLazyLoaded] = React.useState(false) + const onLoadLazy = React.useCallback(() => { + setIsLazyLoaded(true) + }, []) + if (previewImage) { const aspectRatio = previewImage.originalHeight / previewImage.originalWidth @@ -81,60 +85,54 @@ export function LazyImage({ ) } - return ( - // @ts-expect-error LazyImage types are out-of-date. - - {({ imageState, ref }) => { - const isLoaded = imageState === ImageState.LoadSuccess - const wrapperStyle: React.CSSProperties = { - width: '100%' - } - const imgStyle: React.CSSProperties = {} - - if (height) { - wrapperStyle.height = height - } else { - imgStyle.position = 'absolute' - wrapperStyle.paddingBottom = `${aspectRatio * 100}%` - } - - return ( -
- {alt} - - {alt} -
- ) - }} -
- ) + return (() => { + const wrapperStyle: React.CSSProperties = { + width: '100%' + } + const imgStyle: React.CSSProperties = {} + + if (height) { + wrapperStyle.height = height + } else { + imgStyle.position = 'absolute' + wrapperStyle.paddingBottom = `${aspectRatio * 100}%` + } + + return ( +
+ {alt} + + {alt} +
+ ) + })() } else { // TODO: GracefulImage doesn't seem to support refs, but we'd like to prevent // invalid images from loading as error states From e32ae2f0f9eaeccb9caaf5611b2d7a20b3dbdcce Mon Sep 17 00:00:00 2001 From: dogyeong Date: Sat, 31 May 2025 21:30:29 +0900 Subject: [PATCH 2/3] chore: remove react-lazy-images dependency --- packages/react-notion-x/package.json | 1 - pnpm-lock.yaml | 39 ---------------------------- 2 files changed, 40 deletions(-) diff --git a/packages/react-notion-x/package.json b/packages/react-notion-x/package.json index 5f62c66e..2d435c9d 100644 --- a/packages/react-notion-x/package.json +++ b/packages/react-notion-x/package.json @@ -64,7 +64,6 @@ "react-fast-compare": "^3.2.0", "react-hotkeys-hook": "^4.5.1", "react-image": "^4.0.3", - "react-lazy-images": "^1.1.0", "react-modal": "^3.16.3" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 13d0a6bf..a4c19cb4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -252,9 +252,6 @@ importers: react-image: specifier: ^4.0.3 version: 4.1.0(@babel/runtime@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react-lazy-images: - specifier: ^1.1.0 - version: 1.1.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react-modal: specifier: ^3.16.3 version: 3.16.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -4366,9 +4363,6 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} - invariant@2.2.4: - resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} - ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -6235,11 +6229,6 @@ packages: react: '>=16.8' react-dom: '>=16.8' - react-intersection-observer@6.4.2: - resolution: {integrity: sha512-gL6YrkhniA0tIbyDbUterzBwKh61vHR520rsKULel5T37gG4YP07wnWI3WoqOcKK5bKAu0PZB2FHD7/OjawN+w==} - peerDependencies: - react: ^15.0.0 || ^16.0.0 || ^17.0.0 - react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -6249,12 +6238,6 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-lazy-images@1.1.0: - resolution: {integrity: sha512-h5DHFhkMJyh2qsDl3hXWu6d+On10FsgHtRJ+BH7xjgsFOvsqaii9CEwEESqPJrrAiHo1qrN1LgzrV8X3zctHKA==} - peerDependencies: - react: ^15 || ^16 - react-dom: ^15 || ^16 - react-lifecycles-compat@3.0.4: resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} @@ -7296,9 +7279,6 @@ packages: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} - unionize@2.2.0: - resolution: {integrity: sha512-lHXiL6LPVuRYBGCLOdUd4GMHoAGqM0HtYHAZcA6pUEiwN1nk+LEYlh8bud7saeL0bkFntJzCPEPVVJeFm3Cqsg==} - unique-string@2.0.0: resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} engines: {node: '>=8'} @@ -12631,10 +12611,6 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 - invariant@2.2.4: - dependencies: - loose-envify: 1.4.0 - ipaddr.js@1.9.1: {} ipaddr.js@2.2.0: {} @@ -14716,25 +14692,12 @@ snapshots: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - react-intersection-observer@6.4.2(react@19.0.0): - dependencies: - '@babel/runtime': 7.26.0 - invariant: 2.2.4 - react: 19.0.0 - react-is@16.13.1: {} react-is@17.0.2: {} react-is@18.3.1: {} - react-lazy-images@1.1.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0): - dependencies: - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - react-intersection-observer: 6.4.2(react@19.0.0) - unionize: 2.2.0 - react-lifecycles-compat@3.0.4: {} react-modal@3.16.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0): @@ -16051,8 +16014,6 @@ snapshots: unicorn-magic@0.3.0: {} - unionize@2.2.0: {} - unique-string@2.0.0: dependencies: crypto-random-string: 2.0.0 From 994088e1ffe7b7bd3c0541241090ccf1d6a0bffc Mon Sep 17 00:00:00 2001 From: dogyeong Date: Sun, 1 Jun 2025 12:14:45 +0900 Subject: [PATCH 3/3] fix: update lazy image ref to handle cached images correctly --- .../react-notion-x/src/components/lazy-image.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/react-notion-x/src/components/lazy-image.tsx b/packages/react-notion-x/src/components/lazy-image.tsx index eea26840..e607a374 100644 --- a/packages/react-notion-x/src/components/lazy-image.tsx +++ b/packages/react-notion-x/src/components/lazy-image.tsx @@ -63,6 +63,18 @@ export function LazyImage({ setIsLazyLoaded(true) }, []) + const lazyImageRef = React.useCallback( + (image: HTMLImageElement) => { + attachZoomRef?.(image) + + // if the image is cached, we can trigger the onLoadLazy immediately + if (image.complete) { + onLoadLazy() + } + }, + [attachZoomRef, onLoadLazy] + ) + if (previewImage) { const aspectRatio = previewImage.originalHeight / previewImage.originalWidth @@ -119,7 +131,7 @@ export function LazyImage({ className='lazy-image-real' src={src} alt={alt} - ref={attachZoomRef} + ref={lazyImageRef} style={{ ...style, ...imgStyle