Skip to content
This repository was archived by the owner on Apr 15, 2025. It is now read-only.

Commit 9b7f024

Browse files
authored
Merge pull request billmalarky#17 from billmalarky/issue-16
Issue 16
2 parents 6722d08 + d084447 commit 9b7f024

File tree

4 files changed

+103
-4
lines changed

4 files changed

+103
-4
lines changed

lib/imageCacheHoc.js

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,15 +145,61 @@ export default function imageCacheHoc(Image, options = {}) {
145145
// Track component mount status to avoid calling setState() on unmounted component.
146146
this._isMounted = true;
147147

148+
// Set url from source prop
149+
const url = traverse(this.props).get(['source', 'uri']);
150+
148151
// Add a cache lock to file with this name (prevents concurrent <CacheableImage> components from pruning a file with this name from cache).
149-
const fileName = await this.fileSystem.getFileNameFromUrl(traverse(this.props).get(['source', 'uri']));
152+
const fileName = await this.fileSystem.getFileNameFromUrl(url);
150153
FileSystem.lockCacheFile(fileName, this.componentId);
151154

155+
// Init the image cache logic
156+
await this._loadImage(url);
157+
158+
}
159+
160+
/**
161+
*
162+
* Enables caching logic to work if component source prop is updated (that is, the image url changes without mounting a new component).
163+
* See: https://github.com/billmalarky/react-native-image-cache-hoc/pull/15
164+
*
165+
* @param nextProps {Object} - Props that will be passed to component.
166+
*/
167+
async componentWillReceiveProps(nextProps) {
168+
169+
// Set urls from source prop data
170+
const url = traverse(this.props).get(['source', 'uri']);
171+
const nextUrl = traverse(nextProps).get(['source', 'uri']);
172+
173+
// Do nothing if url has not changed.
174+
if (url === nextUrl) return;
175+
176+
// Remove component cache lock on old image file, and add cache lock to new image file.
177+
const fileName = await this.fileSystem.getFileNameFromUrl(url);
178+
const nextFileName = await this.fileSystem.getFileNameFromUrl(nextUrl);
179+
180+
FileSystem.unlockCacheFile(fileName, this.componentId);
181+
FileSystem.lockCacheFile(nextFileName, this.componentId);
182+
183+
// Init the image cache logic
184+
await this._loadImage(nextUrl);
185+
186+
}
187+
188+
/**
189+
*
190+
* Executes the image download/cache logic and calls setState() with to re-render
191+
* component using local file path on completion.
192+
*
193+
* @param url {String} - The remote image url.
194+
* @private
195+
*/
196+
async _loadImage(url) {
197+
152198
// Check local fs for file, fallback to network and write file to disk if local file not found.
153199
const permanent = this.props.permanent ? true : false;
154200
let localFilePath = null;
155201
try {
156-
localFilePath = await this.fileSystem.getLocalFilePathFromUrl(traverse(this.props).get(['source', 'uri']), permanent);
202+
localFilePath = await this.fileSystem.getLocalFilePathFromUrl(url, permanent);
157203
} catch (error) {
158204
console.warn(error); // eslint-disable-line no-console
159205
}
@@ -207,4 +253,4 @@ export default function imageCacheHoc(Image, options = {}) {
207253

208254
};
209255

210-
}
256+
}

tests/CacheableImage.test.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,56 @@ describe('CacheableImage', function() {
296296

297297
});
298298

299+
it('#_loadImage should warn developer on error getting local file path.', () => {
300+
301+
const CacheableImage = imageCacheHoc(Image);
302+
303+
const imageUrl = 'https://img.wennermedia.com/5333a62d-07db-432a-92e2-198cafa38a14-326adb1a-d8ed-4a5d-b37e-5c88883e1989.png';
304+
305+
const cacheableImage = new CacheableImage({ // eslint-disable-line no-unused-vars
306+
source: {
307+
uri: imageUrl
308+
}
309+
});
310+
311+
const testError = new Error('Test error');
312+
313+
cacheableImage.fileSystem.getLocalFilePathFromUrl = () => {
314+
throw testError;
315+
};
316+
317+
// Cache console.warn
318+
const consoleWarnCache = console.warn; // eslint-disable-line no-console
319+
320+
console.warn = sinon.spy(); // eslint-disable-line no-console
321+
322+
cacheableImage._loadImage(imageUrl);
323+
324+
console.warn.should.be.calledWithExactly(testError); // eslint-disable-line no-console
325+
326+
// Re-apply console.warn
327+
console.warn = consoleWarnCache; // eslint-disable-line no-console
328+
329+
});
330+
331+
it('componentWillReceiveProps should not throw any uncaught errors.', () => {
332+
333+
const CacheableImage = imageCacheHoc(Image);
334+
335+
const cacheableImage = new CacheableImage({ // eslint-disable-line no-unused-vars
336+
source: {
337+
uri: 'https://img.wennermedia.com/5333a62d-07db-432a-92e2-198cafa38a14-326adb1a-d8ed-4a5d-b37e-5c88883e1989.png'
338+
}
339+
});
340+
341+
cacheableImage.componentWillReceiveProps({ // eslint-disable-line no-unused-vars
342+
source: {
343+
uri: 'https://img.wennermedia.com/wallpaper1-39bd413b-cb85-4af0-9f33-3507f272e562.jpg'
344+
}
345+
});
346+
347+
});
348+
299349
it('#render with valid props does not throw an error.', () => {
300350

301351
const CacheableImage = imageCacheHoc(Image);

tests/__snapshots__/imageCacheHoc.test.js.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ exports[`CacheableImage renders correctly with placeholder option set 1`] = `
6262
<Image
6363
style={
6464
Object {
65+
"backgroundColor": "#dc143c",
6566
"height": 204,
6667
"width": 150,
6768
}

tests/imageCacheHoc.test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,9 @@ describe('CacheableImage', function() {
184184
}
185185
};
186186

187-
const CacheableImage = imageCacheHoc(Image, optionPlaceholder);
187+
const CacheableImage = imageCacheHoc(Image, {
188+
defaultPlaceholder: optionPlaceholder
189+
});
188190

189191
const tree = renderer.create(
190192
<View style={styles.container}>

0 commit comments

Comments
 (0)