How to cache images in action?

Suppose I have a list of urls:

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

And I want to pre-select n for those so that the transition between images is faster. What I'm doing now in componentWillMount is this:

 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 is defined as follows:

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

and times , zip and take coming from ramda .

Now when I use these URLs internally, react like this:

<img src={url} />

it gets into the browser cache according to the Etag and Expire tags no matter where the URL is used. I also plan to use this to pre-fetch the following images n whenever we click n - 1 inside the view, reuse imgNodes in the same way.

My question is:

  • Is this real idea more than 100 components that will use this idea, but only 1 will be visible at a time?

  • Will I run into memory issues doing this? I assume that imgNodes will collect garbage when the component is unmounted.

We use redux to store these images in storage, but it looks like I'm processing caching instead of using the natural browser cache.

How bad is this idea?

+5
source share
1 answer

You do not need to do this in all of your components. Once the image is uploaded, it will be cached by the browser and will be available in all components, so you can only do this once in a high-level component.

I don’t know what UX you are trying to create by caching images, however, your code initiates the loading of images, but does not know if the image is loading, whether it was loaded successfully or even failed. So, for example, you want to show a button for changing images or add a class to a component only when the images have been loaded (to make them smooth), your current code may let you down.

You can solve this problem with Promises.

 // create an utility function somewhere const checkImage = path => new Promise(resolve => { const img = new Image() img.onload = () => resolve(path) img.onerror = () => reject() img.src = path }) ... // then in your component constructor(props) { super(props) this.state = { imagesLoaded: false } } // doesn't really matter whether you do it in componentWillMount or componentDidMount componentWillMount() { 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 /> 

As for memory consumption - I do not think that something bad will happen. Browsers usually limit the number of concurrent xhr requests, so you won’t be able to create a giant burst of heap usage to destroy anything, since unused images will collect garbage (they are still stored in the browser’s cache).

The Redux store is a place to store the state of the application, not the assets of the application, but in any case, you will not be able to store any real images there.

+1
source

Source: https://habr.com/ru/post/1267204/


All Articles