Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How To Cache Images in React?

Suppose I have a list of url's like so :

[ '/images/1', '/images/2', ... ]

And I want to prefetch n of those so that transitioning between images is faster. What I am doing now in componentWillMount is the following:

componentWillMount() {
      const { props } = this;
      const { prefetchLimit = 1, document = dummyDocument, imgNodes } = props;
      const { images } = document;
      const toPrefecth = take(prefetchLimit, images);
      const merged = zip(toPrefecth, imgNodes);

      merged.forEach(([url, node]) => {
        node.src = url;
      });
    }

with imgNodes being defined like so:

imgNodes: times(_ => new window.Image(), props.prefetchLimit),

and times, zip, and take coming from ramda.

Now when I use those urls inside of react like so:

<img src={url} />

it hits the browser cache according to the Etag and Expire tags regardless of where the url is used. I also plan on using this to prefetch the next n images whenever we hit n - 1 inside of the view, reusing imgNodes in the same manner.

My question are:

  • Is this even a valid idea give 100+ components that will use this idea but only 1 will be visible at a time?

  • Will I run into memory issues by doing this? I am assuming that imgNodes will be garbage collected when the component is unmounted.

We are using redux so I could save these images in the store but that seems like I am handling the caching instead of leveraging the browser's natural cache.

How bad of an idea is this?

like image 890
Tim Roberts Avatar asked Apr 26 '17 21:04

Tim Roberts


People also ask

Does React Native cache images?

react-native-fast-image is a performant React Native component for loading images. FastImage aggressively caches all loaded images. You can add your own request auth headers and preload images. react-native-fast-image even has GIF caching support.

What is image caching?

When you cache an image service, the server draws the image at a set of scale levels and pixel sizes that you define and saves the preprocessed (cached) images. So, when the server receives a request for an image, it's much quicker to return one of these cached images than to draw the original image again.


1 Answers

You don't need to do it in all of your components. As soon as an image is downloaded it gets cached by the browser and will be accessible in all components, so you can do this only once somewhere in a high-level component.

I don't know what exactly UX you are trying to create by caching images, however, your code only initiates downloading images but doesn't know whether an image is being downloaded, has been downloaded successfully or even failed. So, for example, you want to show a button to change images or add a class to a component only when the images have been downloaded (to make it smooth), your current code may let you down.

You may want to resolve this with Promises.

// create an utility function somewhere
const checkImage = path =>
    new Promise((resolve, reject) => {
        const img = new Image()
        img.onload = () => resolve(path)
        img.onerror = () => reject()

        img.src = path
    })

...

// then in your component
class YourComponent extends Component {

    this.state = { imagesLoaded: false }

    componentDidMount = () =>
        Promise.all(
            R.take(limit, imgUrls).map(checkImage)
        ).then(() => this.setState(() => ({ imagesLoaded: true })),
               () => console.error('could not load images'))

    render = () =>
        this.state.imagesLoaded
            ? <BeautifulComponent />
            : <Skeleton />
}

Regarding memory consumption — I don't think anything bad will happen. Browsers normally limit the number of parallel xhr requests, so you won't be able to create a gigantic heap usage spike to crash anything, as unused images will garbage collected (yet, preserved in browser cache).

Redux store is a place to store the app state, not the app assets, but anyway you won't be able to store any actual images there.

like image 198
dehumanizer Avatar answered Sep 28 '22 00:09

dehumanizer