Skip to content

Commit 9c0feb7

Browse files
committed
[change] Image - extract useSource hook
Move the image loading effect here Changed the original logic slightly for less nesting Changed to cover cases where passing the same headers object was starting new loads, as it was treated as a different value due to referential equality
1 parent 6e26902 commit 9c0feb7

File tree

1 file changed

+81
-59
lines changed
  • packages/react-native-web/src/exports/Image

1 file changed

+81
-59
lines changed

packages/react-native-web/src/exports/Image/index.js

Lines changed: 81 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -185,18 +185,10 @@ const Image: React.AbstractComponent<
185185
);
186186
}
187187
}
188-
const [loadedUri, setLoadedUri] = React.useState('');
189-
const [state, updateState] = React.useState(() => {
190-
const uri = resolveAssetUri(source);
191-
if (uri != null) {
192-
const isLoaded = ImageLoader.has(uri);
193-
if (isLoaded) {
194-
return LOADED;
195-
}
196-
}
197-
return IDLE;
198-
});
199-
188+
const { state, loadedUri } = useSource(
189+
{ onLoad, onLoadStart, onLoadEnd, onError },
190+
source
191+
);
200192
const [layout, updateLayout] = React.useState({});
201193
const hasTextAncestor = React.useContext(TextAncestorContext);
202194
const hiddenImageRef = React.useRef(null);
@@ -254,53 +246,6 @@ const Image: React.AbstractComponent<
254246
}
255247
}
256248

257-
// Image loading
258-
const uri = resolveAssetUri(source);
259-
let headers;
260-
if (source && typeof source.headers === 'object') {
261-
headers = ((source.headers: any): { [key: string]: string });
262-
}
263-
264-
React.useEffect(() => {
265-
if (uri != null) {
266-
updateState(LOADING);
267-
if (onLoadStart) onLoadStart();
268-
269-
const requestId = ImageLoader.load(
270-
{ uri, headers },
271-
function load(result) {
272-
updateState(LOADED);
273-
setLoadedUri(result.uri);
274-
if (onLoad) {
275-
onLoad(result);
276-
}
277-
if (onLoadEnd) {
278-
onLoadEnd();
279-
}
280-
},
281-
function error() {
282-
updateState(ERRORED);
283-
if (onError) {
284-
onError({
285-
nativeEvent: {
286-
error: `Failed to load resource ${uri} (404)`
287-
}
288-
});
289-
}
290-
if (onLoadEnd) {
291-
onLoadEnd();
292-
}
293-
}
294-
);
295-
296-
const effectCleanup = () => {
297-
if (requestId) ImageLoader.release(requestId);
298-
};
299-
300-
return effectCleanup;
301-
}
302-
}, [updateState, onError, onLoad, onLoadEnd, onLoadStart, uri, headers]);
303-
304249
return (
305250
<View
306251
{...rest}
@@ -351,6 +296,83 @@ ImageWithStatics.queryCache = function (uris) {
351296
return ImageLoader.queryCache(uris);
352297
};
353298

299+
type UseSourceParams = {
300+
onLoad?: Function,
301+
onLoadStart?: Function,
302+
onLoadEnd?: Function,
303+
onError?: Function
304+
};
305+
306+
/**
307+
* Image loading/state management hook
308+
* @param params
309+
* @param source
310+
* @returns {{state: string, uri: string}}
311+
*/
312+
const useSource = (
313+
{ onLoad, onLoadStart, onLoadEnd, onError }: UseSourceParams,
314+
source: ?Source
315+
): { state: string, uri: string } => {
316+
const [loadedUri, setLoadedUri] = React.useState('');
317+
const [state, updateState] = React.useState(() => {
318+
const uri = resolveAssetUri(source);
319+
if (uri != null) {
320+
const isLoaded = ImageLoader.has(uri);
321+
if (isLoaded) return LOADED;
322+
}
323+
return IDLE;
324+
});
325+
326+
const loadInput = React.useRef(null);
327+
328+
React.useEffect(() => {
329+
const uri = resolveAssetUri(source);
330+
if (uri == null) return;
331+
332+
let headers;
333+
if (source && typeof source.headers === 'object') {
334+
headers = ((source.headers: any): { [key: string]: string });
335+
}
336+
337+
const nextInput = { uri, headers };
338+
const currentInput = loadInput.current;
339+
if (JSON.stringify(nextInput) === JSON.stringify(currentInput)) return;
340+
341+
updateState(LOADING);
342+
if (onLoadStart) onLoadStart();
343+
344+
loadInput.current = nextInput;
345+
const requestId = ImageLoader.load(
346+
nextInput,
347+
function load(result) {
348+
updateState(LOADED);
349+
setLoadedUri(result.uri);
350+
if (onLoad) onLoad(result);
351+
if (onLoadEnd) onLoadEnd();
352+
},
353+
function error() {
354+
updateState(ERRORED);
355+
if (onError) {
356+
onError({
357+
nativeEvent: {
358+
error: `Failed to load resource ${uri} (404)`
359+
}
360+
});
361+
}
362+
if (onLoadEnd) onLoadEnd();
363+
}
364+
);
365+
366+
const effectCleanup = () => ImageLoader.release(requestId);
367+
return effectCleanup;
368+
}, [updateState, onError, onLoad, onLoadEnd, onLoadStart, source]);
369+
370+
return {
371+
state,
372+
loadedUri
373+
};
374+
};
375+
354376
const styles = StyleSheet.create({
355377
root: {
356378
flexBasis: 'auto',

0 commit comments

Comments
 (0)