[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
This commit is contained in:
Peter Velkov
2023-04-12 16:35:46 -07:00
committed by Nicolas Gallagher
parent e722282c09
commit 7cb5800e25
3 changed files with 25 additions and 32 deletions
@@ -255,23 +255,6 @@ exports[`components/Image prop "source" is correctly updated when missing in ini
</div>
`;
exports[`components/Image prop "source" is not set immediately if the image has not already been loaded 1`] = `
<div
class="css-view-175oi2r r-flexBasis-1mlwlqe r-overflow-1udh08x r-zIndex-417010"
>
<div
class="css-view-175oi2r r-backgroundColor-1niwhzg r-backgroundPosition-vvn4in r-backgroundRepeat-u6sd8q r-bottom-1p0dtai r-height-1pi2tsx r-left-1d2f490 r-position-u8s1d r-right-zchlnj r-top-ipm5af r-width-13qz1uu r-zIndex-1wyyakw r-backgroundSize-4gszlv"
style="background-image: url(https://google.com/favicon.ico);"
/>
<img
alt=""
class="css-accessibilityImage-9pa8cd"
draggable="false"
src="https://google.com/favicon.ico"
/>
</div>
`;
exports[`components/Image prop "source" is set immediately if the image has already been loaded 1`] = `
<div
class="css-view-175oi2r r-flexBasis-1mlwlqe r-overflow-1udh08x r-zIndex-417010"
@@ -323,6 +306,23 @@ exports[`components/Image prop "source" is set immediately if the image was prel
</div>
`;
exports[`components/Image prop "source" is set immediately while image is loading and there is no default source 1`] = `
<div
class="css-view-175oi2r r-flexBasis-1mlwlqe r-overflow-1udh08x r-zIndex-417010"
>
<div
class="css-view-175oi2r r-backgroundColor-1niwhzg r-backgroundPosition-vvn4in r-backgroundRepeat-u6sd8q r-bottom-1p0dtai r-height-1pi2tsx r-left-1d2f490 r-position-u8s1d r-right-zchlnj r-top-ipm5af r-width-13qz1uu r-zIndex-1wyyakw r-backgroundSize-4gszlv"
style="background-image: url(https://google.com/not-yet-loaded-image.ico);"
/>
<img
alt=""
class="css-accessibilityImage-9pa8cd"
draggable="false"
src="https://google.com/not-yet-loaded-image.ico"
/>
</div>
`;
exports[`components/Image prop "style" removes other unsupported View styles 1`] = `
<div
class="css-view-175oi2r r-flexBasis-1mlwlqe r-overflow-1udh08x r-zIndex-417010"
@@ -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(<Image source={source} />);
expect(container.firstChild).toMatchSnapshot();
+6 -13
View File
@@ -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();