Hi,
It seems that when you use the React Native Image component you get caching for free - about 5MB. But it seems that what gets cached is the decoded image object - so if you have, for example, a 500x750 image, napkin math (5007504 bits) says that’s going to be 1.5Meg, meaning only three images can be cached. That’s how I interpret the following code, from the React Native GitHub RCTImageCache.m:
@implementation RCTImageCache
{
NSOperationQueue *_imageDecodeQueue;
NSCache *_decodedImageCache;
}
- (instancetype)init
{
_decodedImageCache = [NSCache new];
_decodedImageCache.totalCostLimit = 5 * 1024 * 1024; // 5MB
So only 3 images! Do I have this right? What are people doing to get around this - is there an expo friendly cacheing library out there? Should I roll my own?
ideally i think it would be a file system based cache, since that would store the compressed jpg, and have a fixed size with older files being evicted when it was exceeded.
@marstall Unfortunately, we do not have this fully documented, but you can look through our code, where we have caching associated with assets that are on filesystem.
https://docs.expo.io/versions/latest/guides/assets.html
https://docs.expo.io/versions/latest/sdk/asset.html
I solved this with a CachedImage component which stores the images in AsyncStorage:
import React, { Component } from 'react'
import { Image, View } from 'react-native'
export default class CachedImage extends Image {
constructor (props) {
super(props)
const {uri} = this.props
this.state = {}
this.get(uri).then((imageData) => {
this.setState({imageData})
})
}
static async get (url) {
var data = await AsyncStorage.getItem(url) // get it from disk
if (!data) { // if it's not on the disk
const response = await fetch(url) // get it from the network
data = response._bodyInit
await AsyncStorage.setItem(url, data) // set it to disk
}
return data
}
render () {
const {imageData = null} = this.state
const {placeholderStyle = styles.placeholder} = this.props
if (imageData != null) {
return <Image {...this.props} source={{uri: `data:image/png;base64,${imageData}`}}/>
} else {
return <View/>
}
}
}
this relies on your image server serving images in base64 encoded plaintext (which was the tricky part). For me, using rails, that effectively looks like this on the back end:
data = open(url)
send_data Base64.encode64(data.read), type: 'text/plain;base64', disposition: 'inline'
@wcandillon awesome! Nice writeup, learned somethin this morning
1 Like