GithubHelp home page GithubHelp logo

billmalarky / react-native-image-cache-hoc Goto Github PK

View Code? Open in Web Editor NEW
287.0 5.0 56.0 1.08 MB

React Native Higher Order Component that adds advanced caching functionality to the react native Image component.

License: MIT License

JavaScript 100.00%

react-native-image-cache-hoc's Introduction

React Native Image Cache HOC

Build Status License ESLint Coverage Status

React Native Higher Order Component that adds advanced caching functionality to the react native Image component.

Features

  • Drop in Replacement for native <Image> component.
  • Automatically Cache remote image files to local filesystem to increase performance.
  • Automatically Persist remote image files to local filesystem forever with a simple component prop flag.

Installation

$ npm install --save react-native-image-cache-hoc

Or

$ yarn add react-native-image-cache-hoc

Then, because this package has a depedency on rn-fetch-blob you will need to link this native package by running:

$ react-native link rn-fetch-blob

Linking rn-fetch-blob should only be done once, reinstalling node_modules with npm or yarn does not require running the above command again.

To troubleshoot linking, refer to the rn-fetch-blob installation instructions.

Usage

React Native Image Cache HOC creates an advanced image component, <CacheableImage>, that is a drop in replacement for the standard <Image> component.

Differences between the advanced image component and standard image component API are as follows:

  1. Modified "source" Prop: The advanced component "source" prop only accepts a web accessible url (there's no reason to use this library to render files that already exist on the local filesystem), and it does NOT accept an array of urls.
  2. New "permanent" Prop: The new, optional (defaults to False), "permanent" prop determines if the image file should be stored forever on the local filesystem instead of written to a temperary cache that is subject to occasional pruning.
  3. New "placeholder" Prop: The new, optional (defaults to standard Image component), "placeholder" prop determines component to render while remote image file is downloading.

TL;DR: To cache image files for performance, simply use <CacheableImage> as a drop in replacement for <Image>. To store files permanently add a permanent={true} prop to <CacheableImage>.

import React, { Component } from 'react';
import {
  StyleSheet,
  Text,
  View,
  Image
} from 'react-native';

import imageCacheHoc from 'react-native-image-cache-hoc';

/**
* Pass the native <Image> component into imageCacheHoc() to create the advanced image component <CacheableImage>.
* 
* imageCacheHoc() takes an options object as the second parameter (refer to options section of README.md)
*/
const CacheableImage = imageCacheHoc(Image, {
  fileHostWhitelist: ['i.redd.it']
});

export default class App extends Component<{}> {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>Welcome to React Native!</Text>
        <CacheableImage style={styles.image} source={{uri: 'https://i.redd.it/rc29s4bz61uz.png'}} permanent={false} />
      </View>
  );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  image: {
    width:150,
    height: 204
  }
});

Options

React Native Image Cache HOC accepts an options object in order to tweak standard functionality.

imageCacheHoc(Image, {
  
  // Allow http urls. 
  // Defaults to https only.
  validProtocols: ['http', 'https'],
  
  // Use domain host whitelist. 
  // Defaults to allowing urls from all domain hosts.
  fileHostWhitelist: ['localhost', 'i.redd.it'],
  
  // Namespace the directory that stores files to avoid collisions with other app libraries. 
  // Defaults to 'react-native-image-cache-hoc'.
  fileDirName: 'example-app-files-namespace',
  
  // Max size of file cache in bytes before pruning occurs. 
  // Note that cache size can exceed this limit, 
  // but sequential writes to the cache will trigger cache pruning 
  // which will delete cached files until total cache size is below this limit before writing.
  // Defaults to 15 MB.
  cachePruneTriggerLimit: 1024 * 1024 * 10,
  
  // Default placeholder component to render while remote image file is downloading. 
  // Can be overridden with placeholder prop like <CacheableImage placeholder={placeHolderObject} />. 
  //
  // Placeholder Object is structed like:
  // const placeHolderObject = {
  //   component: ReactComponentToUseHere,
  //    props: {
  //      examplePropLikeStyle: componentStylePropValue,
  //      anotherExamplePropLikeSource: componentSourcePropValue
  //   }
  // };
  //
  // Defaults to <Image> component with style prop passed through.
  defaultPlaceholder: {
    component: ActivityIndicator,
    props: {
      style: activityIndicatorStyle
    }
  }
  
});

Using Loading Placeholders

React Native Image Cache HOC allows you to easily supply any component to be used as a placeholder while the remote image file is downloading. While the default placeholder should be great for many use cases, you can easily use your own to match the style of the rest of your app.

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  image: {
    width:150,
    height: 204
  },
  activityIndicatorStyle: {
    width: 150,
    height: 204,
    backgroundColor: '#dc143c'
  }
});

// This placeholder object will be used as a placeholder component for all instances of <CacheableImage> 
// unless individual <CacheableImage> uses "placeholder" prop to override this default.
const defaultPlaceholderObject = {
  component: ActivityIndicator,
  props: {
    style: styles.activityIndicatorStyle
  }
};

// We will use this placeholder object to override the default placeholder.
const propOverridePlaceholderObject = {
  component: Image,
  props: {
    style: styles.image,
    source: {require('./localPlaceholderImage.png')}
  }
};

const CacheableImage = imageCacheHoc(Image, {
  defaultPlaceholder: defaultPlaceholderObject
});

export default class App extends Component<{}> {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>Welcome to React Native!</Text>
        <CacheableImage style={styles.image} source={{uri: 'https://i.redd.it/rc29s4bz61uz.png'}} />
        <CacheableImage style={styles.image} source={{uri: 'https://i.redd.it/hhhim0kc5swz.jpg'}} placeholder={propOverridePlaceholderObject} />
        <CacheableImage style={styles.image} source={{uri: 'https://i.redd.it/17ymhqwgbswz.jpg'}} />
      </View>
  );
  }
}

Static Methods

The CacheableImage class returned by React Native Image Cache HOC includes a couple of static methods for convenience.

CacheableImage.cacheFile(url, permanent)

Use this method if you need to download a file to the local filesystem prior to rendering <CacheableImage> for some reason (perhaps to pre-warm the local cache). If calling this method repeatedly to cache a long list of files, be sure to use a queue and limit concurrency so your app performance does not suffer.

import imageCacheHoc from 'react-native-image-cache-hoc';
const CacheableImage = imageCacheHoc(Image);
CacheableImage.cacheFile('https://i.redd.it/17ymhqwgbswz.jpg')
  .then( localFileInfo => {
    console.log(localFileInfo);
    // The https://i.redd.it/17ymhqwgbswz.jpg remote file is now saved to local fs. 
    // Since permanent was not set, this file is subject to cache pruning.
  });

CacheableImage.cacheFile('https://i.redd.it/hhhim0kc5swz.jpg', true)
  .then( localFileInfo => {
    console.log(localFileInfo);
    // The https://i.redd.it/hhhim0kc5swz.jpg remote file is now saved to local fs permanently.
  });

CacheableImage.flush()

Delete all locally stored image files created by react-native-image-cache-hoc (cache AND permanent). Calling this method will cause a performance hit on your app until the local files are rebuilt.

import imageCacheHoc from 'react-native-image-cache-hoc';
const CacheableImage = imageCacheHoc(Image);
CacheableImage.flush()
  .then( flushResults => {
    console.log(flushResults);
    // All local filles created by 'react-native-image-cache-hoc' are now destroyed. 
    // They will be rebuilt by future <CacheableImage> renders.
  });

Jest Test Support

React Native Image Cache HOC must be run in a native environment to work correctly. As a result it will create issues in your jest tests unless you mock it. Since this module is an HOC that adds additional functionality to the standard <Image> component, it can be easily mocked with a function that returns the standard <Image> component.

Add the following to your jest mocks:

jest.mock('react-native-image-cache-hoc', () => {

  const mockComponent = require('react-native/jest/mockComponent');
  const MockCacheableImage = mockComponent('Image');
  
  // Add mock static methods
  // To see how to use jest.fn() to return mock data in your tests see the following:
  // https://facebook.github.io/jest/docs/en/mock-function-api.html
  MockCacheableImage.cacheFile = jest.fn(); 
  MockCacheableImage.flush = jest.fn();

  return function() {
    return MockCacheableImage;
  }

});

Warning

iOS only allows requests to https urls. If you need to load image files using http you will need to make additional react native config changes.

By default, iOS will block any request that's not encrypted using SSL. If you need to fetch from a cleartext URL (one that begins with http) you will first need to add an App Transport Security exception. If you know ahead of time what domains you will need access to, it is more secure to add exceptions just for those domains; if the domains are not known until runtime you can disable ATS completely. Note however that from January 2017, Apple's App Store review will require reasonable justification for disabling ATS.

https://facebook.github.io/react-native/docs/network.html

react-native-image-cache-hoc's People

Contributors

adamivancza avatar billmalarky avatar erickreutz avatar khrizt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

react-native-image-cache-hoc's Issues

Make rn-fetch-blob a peer dependency

In the same way that react-native is not a direct dependency, rn-fetch-blob should be a peer dependency.

Users may already have a direct dependency on this in their project - ideally this wouldn't be duplicated. It also allows users to use other (compatible) versions of rn-fetch-blob which may include bug fixes.

It can be added as a dev dependency for the tests to run, if necessary. The minimum compatible react-native version should also be specified as a peer dependency.

Warning on setState

Hey @billmalarky

Sorry for being so verbose, but I found that in the line

localFilePath => this.setState({ localFilePath }),
of imageCacheHoc file there is a setState without checking if the component is mounted. Maybe keeping a flag and setting to true in componentDidMount and false in componentDidUnmount would be a solution.

It happens of images that I show and hide real fast, I'm using it inside a chat app so this can happen. I can do a PR if you think it's a good idea.

Clear cache

Hello,

I was wondering if it's possible to clear the cache. For example when a user disconnects and then another one connects on the same device, I would like to clear the cache because the other account doesn't need the images that were stored in cache from the first account.

Thanks a lot.

Package abandoned?

There haven't been any commits for ~2.5 years and there are a number of pretty breaking issues #22 #26 #32 #45, do you intend on supporting this package going forward?
If this is abandoned would you consider letting someone else take it over?
I've ended up forking this package to add a few enhancements and quite a few changes (and some breaking ones), if this package is abandoned I think I may make my fork standalone.

https://github.com/mnightingale/react-native-image-cache-hoc/projects/1 outlines changes and ideas for future enhancements, granted I haven't made any updates for a while I've just added features as and when I needed them and the readme needs updates.

Notable changes:

  • Replace permanent/cache with immutable/mutable
    • immutable - download only if not in cache (same as existing behaviour regardless of permanent value)
    • mutable - download if not in cache, else respect http headers (sends if-modified-since)
  • Deduplication of requests (RxJS), multiple components can subscribe to the same URL, only one download request will be made and the components will be notified when the local uri changes
  • Switch to react-native-fs (resolves image loading issues and I prefer the api and behaviour)
  • Get file extension from path

HTTP Source images not supported

Hello

I an getting following error upon initialization of CacheableImage

i.e Invalid source prop. props.source.uri should be a web accessible url with a valid protocol and host. NOTE: Default valid protocol is https, default valid hosts are *.

Could you tell me if current library supports http protocol links.

The image can't be cache if the link is in HTTP

Based on the title,

The problem keep persist in android. Meanwhile when I try on ios it works perfectly fine.. It never mention any error just a warning.. But the image is not displaying on android because of this warn.. I did try allow the android:usesCleartextTraffic = true and other instruction based on this thread but the problem still persist..

Here's one of http image

and here's the warn display:

1569575738033

1569575737999

all of the above problem is when i use this :

CacheableImage.cacheFile('https://i.redd.it/hhhim0kc5swz.jpg', true) .then( localFileInfo => { console.log(localFileInfo); });

if i use this function:

<CacheableImage style={styles.image} source={{uri: 'https://i.redd.it/rc29s4bz61uz.png'}} permanent={false} />

this the warn i got :

Screenshot_1569578222

Screenshot_1569578230

I did try trace the 'EUNSPECIFIED' in the module itself and try to log one by one but the log never appear on the 'EUNSPECIFIED' section..

Did I do wrong somewhere?
Can anyone help me with this problem? Thanks in advance

Warning

Hi,

Thanks for this great library. Right now I'm adding it to our react native app and sometimes I see this warning:

Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState or forceUpdate on an unmounted component. This is a no-op.

on line 151 of imageCacheHoc.js. At least that's what it says :). Am I doing something wrong or is there a way to fix that? Thanks

Console warning

First of all, thank you for this project 🙏

I am getting a console waring in the app for each image.
"Received data was not a string, or was not a recognised encoding"

The only thing I found searching for a the cause of this warning was this issue on react-native facebook/react-native#1780 (comment)

"react-native": "^0.49.5",
"react-native-image-cache-hoc": "^1.4.0",

[TypeError: Network request failed] in android

"react-native": "0.61.5",
"react-native-image-cache-hoc": "^2.0.0",
"rn-fetch-blob": "^0.12.0"

I get [TypeError: Network request failed] when I try to load image in android.

What have I tried?
Updated android manifest with android:usesCleartextTraffic="true" and deleted debug folder as mentioned in thread in rn-fetch-blob but no luck.

Can you please help me with this issue

Permanent CacheableImages should not pull from local cache dir only permanent dir

Currently FileSystem.getLocalFilePathFromUrl() checks if a file exists in either the local cache or permanent directories before hitting the network and saving to the appropriate local dir.

If the permanent prop flag is set to true, we should hit the network even if the file already exists in the local cache dir because cache dir files are ephemeral and the permanent prop should not rely on them (what if the user wants this image to render in a future render even if at that point in time the app does not have internet access).

Better yet, if permanent prop is set and the file exists in the cache dir but not the permanent dir, ignore the network and just copy the local file from cache dir to permanent dir.

removing dep on react-native-fetch-blob?

react-native-fetch-blob has no maintainer, and a ton of issues.
On a fresh project with React Native 0.54 I couldn't get RNFB working after following manual linking instructions etc.
Are you using a lot of complicated functionality of RNFB or would it be possible to use just boring old fetches?

[Improvements] Request headers, filename, rejection callback

Hi there,

I forked your library for a project and added some (in my case) improvements:

  • Component takes a headers-property (needed for authorization headers in my specific case)
  • Component takes a fileName-property (I did not have the filename in the URL, and fetching the extension can slower than is desirable)
  • Component takes a rejected-component, similar to the placeholder that has already been implemented
  • Component takes a onRejected-property that gets called when fetching the image fails.

These additions should be perfectly backwards compatible.

Additional changes I want to implement in my fork, but would not be backwards-compatible:

  • Changing the placeholder props and options into a more standard pattern (slots or render-function)
    <CacheableImage
       placeholder={
         <Spinner />
       }/>

or

    <CacheableImage
      placeholder={
        () => <Spinner/>
      }/>
  • I was thinking of maybe changing up the code somewhat so the library could be changed to export both a FAC- and a HOC-component, but this would require some larger rearrangements of code.
    I feel this would make the library more flexible for advanced users.
    FAC-example
    <CacheableImage
      source="my.resource.com">
      {({pending, source, isRejected}) => 
        pending ? 
          <Spinner/> : 
          <Image source={source}/>
      }
    </CacheableImage>

I can totally make a pull request (or two!) for these, so they can be merged and published under this package, if those changes are something you see fitting here!

Cheers!

Warning on last version

Hi @billmalarky

I've updated to the last library version and sometimes I get the following warning:

TypeError: undefined is not a function (evaluating 'this.cancelLocalFilePathRequest()')

Looks like sometimes the function is not initialized. It occurs when a view that has a ImageCacheHoc disappears quickly.

And btw, I have a question, the images that are stored in the cache (not permanently) should be visible from the DocumentDir folder? The problem is in a app screen that has a lot of images, I always get the placeholder but looking the code if it's a local file and already exists it should display the image directly from the local fs. But I don't know if it's the correct behaviour.

Thanks

Unable to determine remote image filetype

When i try to load images with uri without file extension like .png .jpg. i get this error. For example
https://s3.us-east-2.amazonaws.com/my-files/da10e0d5-a781-4601-b40e-304459fbbd61

Error: Unable to determine remote image filetype.
    at FileSystem.getFileNameFromUrl$ (blob:http://localhost:8081/bdbc2931-ac7a-4ccf-babd-9e95180ae711:110388:23)

Any help will be highly appreciated. Anyway great package.

Caches images non-unique naming

Sometimes react-native-image-cache-hoc throws Unhandled promise rejection error, android studio debugger shows that
'Unhandled promise rejection', { [Error: File ...filename.png already exists]

app showing error while http url images in ios and android platform

I implemented react-native-image-cache-hoc. When I run code HTTP URL images are not showing even I modified my info.plist file with arbitrary loads true.
this error I am getting always -
Error with task : Error: Invalid source prop. props.source.uri should be a web-accessible URL with a valid protocol and host. NOTE: Default valid protocol is https, default valid hosts are *.

I used also this -
export const CacheableImage = imageCacheHoc(Image, {
validProtocols: ['http','https']
});

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.