Image cache only caches a few images?


#1

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?


Image are displayed only compressed
#2

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.


#3

@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


#4

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'


#5

I just finished an expo component for image caching at https://github.com/wcandillon/react-native-expo-image-cache. Image caching is a delicate topic and wrote about common pitfalls at https://medium.com/@wcandillon/5-things-to-know-about-images-react-native-69be41d2a9ee


#6

@wcandillon awesome! Nice writeup, learned somethin this morning :slight_smile: