From 274b316016ff9513008a5e1d1736ac442ac90412 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Fri, 24 Feb 2023 20:15:10 +0200 Subject: [PATCH] [fix] Image: image LOADED state is only captured initially If the Image component is rendered with a `null` source, and consecutively updated with actual source url that was already loaded, it would fail to pick up the change - `state` would be `IDLE` for a brief moment and this would cause a small flicker when the image renders Let's always start from IDLE state, and update `shouldDisplaySource` condition to be based on `ImageLoader.has` cache or not Fix #2492 --- .../__snapshots__/index-test.js.snap | 20 +++++++++---------- .../src/exports/Image/__tests__/index-test.js | 4 ++-- .../src/exports/Image/index.js | 19 ++++++------------ 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/packages/react-native-web/src/exports/Image/__tests__/__snapshots__/index-test.js.snap b/packages/react-native-web/src/exports/Image/__tests__/__snapshots__/index-test.js.snap index b7314426d..c87e95253 100644 --- a/packages/react-native-web/src/exports/Image/__tests__/__snapshots__/index-test.js.snap +++ b/packages/react-native-web/src/exports/Image/__tests__/__snapshots__/index-test.js.snap @@ -255,7 +255,7 @@ exports[`components/Image prop "source" is correctly updated when missing in ini `; -exports[`components/Image prop "source" is not set immediately if the image has not already been loaded 1`] = ` +exports[`components/Image prop "source" is set immediately if the image has already been loaded 1`] = `
@@ -272,53 +272,53 @@ exports[`components/Image prop "source" is not set immediately if the image has
`; -exports[`components/Image prop "source" is set immediately if the image has already been loaded 1`] = ` +exports[`components/Image prop "source" is set immediately if the image has already been loaded 2`] = `
`; -exports[`components/Image prop "source" is set immediately if the image has already been loaded 2`] = ` +exports[`components/Image prop "source" is set immediately if the image was preloaded 1`] = `
`; -exports[`components/Image prop "source" is set immediately if the image was preloaded 1`] = ` +exports[`components/Image prop "source" is set immediately while image is loading and there is no default source 1`] = `
`; diff --git a/packages/react-native-web/src/exports/Image/__tests__/index-test.js b/packages/react-native-web/src/exports/Image/__tests__/index-test.js index 0e75e792e..abfae4787 100644 --- a/packages/react-native-web/src/exports/Image/__tests__/index-test.js +++ b/packages/react-native-web/src/exports/Image/__tests__/index-test.js @@ -277,8 +277,8 @@ describe('components/Image', () => { }); }); - test('is not set immediately if the image has not already been loaded', () => { - const uri = 'https://google.com/favicon.ico'; + test('is set immediately while image is loading and there is no default source', () => { + const uri = 'https://google.com/not-yet-loaded-image.ico'; const source = { uri }; const { container } = render(); expect(container.firstChild).toMatchSnapshot(); diff --git a/packages/react-native-web/src/exports/Image/index.js b/packages/react-native-web/src/exports/Image/index.js index 64b7a7327..c89dd0ab3 100644 --- a/packages/react-native-web/src/exports/Image/index.js +++ b/packages/react-native-web/src/exports/Image/index.js @@ -225,24 +225,18 @@ const BaseImage: ImageComponent = React.forwardRef((props, ref) => { } } - const [state, updateState] = React.useState(() => { - const uri = resolveAssetUri(source); - if (uri != null) { - const isLoaded = ImageLoader.has(uri); - if (isLoaded) { - return LOADED; - } - } - return IDLE; - }); - + const [state, updateState] = React.useState(IDLE); const [layout, updateLayout] = React.useState({}); const hasTextAncestor = React.useContext(TextAncestorContext); const hiddenImageRef = React.useRef(null); const filterRef = React.useRef(_filterId++); const requestRef = React.useRef(null); + const uri = resolveAssetUri(source); + const isCached = uri != null && ImageLoader.has(uri); const shouldDisplaySource = - state === LOADED || (state === LOADING && defaultSource == null); + state === LOADED || + isCached || + (state === LOADING && defaultSource == null); const [flatStyle, _resizeMode, filter, _tintColor] = getFlatStyle( style, blurRadius, @@ -297,7 +291,6 @@ const BaseImage: ImageComponent = React.forwardRef((props, ref) => { } // Image loading - const uri = resolveAssetUri(source); React.useEffect(() => { abortPendingRequest();