wagslane / react-native-expo-cached-image

Cached image component for Expo's managed workflow
https://qvault.io/2020/02/04/how-to-cache-images-react-native-expo-managed
MIT License
44 stars 8 forks source link

Allow local images, and don't cache them #3

Closed wagslane closed 3 years ago

wagslane commented 4 years ago

As per https://github.com/lane-c-wagner/react-native-expo-cached-image/issues/2#issuecomment-619715528, it is feasible that one would want to use this component to render local images.

charles-goode commented 4 years ago

Check this out:

import React, { Component } from 'react';
import { View, Image, ImageBackground } from 'react-native';
import * as FileSystem from 'expo-file-system';
import * as Crypto from 'expo-crypto';

export default class CachedImage extends Component {
  state = {
    imgURI: ''
  }

  async componentDidMount() {
    if (this.props.source.uri) {
        const filesystemURI = await this.getImageFilesystemKey(this.props.source.uri);
        await this.loadImage(filesystemURI, this.props.source.uri);
    }

  }

  async componentDidUpdate() {
    if (this.props.source.uri) {
        const filesystemURI = await this.getImageFilesystemKey(this.props.source.uri);
        if (this.props.source.uri === this.state.imgURI ||
          filesystemURI === this.state.imgURI) {
          return null;
        }
        await this.loadImage(filesystemURI, this.props.source.uri);
    }

  }

  async getImageFilesystemKey(remoteURI) {
    const hashed = await Crypto.digestStringAsync(
      Crypto.CryptoDigestAlgorithm.SHA256,
      remoteURI
    );
    return `${FileSystem.cacheDirectory}${hashed}`;
  }

  async loadImage(filesystemURI, remoteURI) {
    try {
      // Use the cached image if it exists
      const metadata = await FileSystem.getInfoAsync(filesystemURI);
      if (metadata.exists) {
        this.setState({
          imgURI: filesystemURI
        });
        return;
      }

      // otherwise download to cache
      const imageObject = await FileSystem.downloadAsync(
        remoteURI,
        filesystemURI
      );
      this.setState({
        imgURI: imageObject.uri
      });
    }
    catch (err) {
      console.log('Image loading error:', err);
      this.setState({ imgURI: remoteURI });
    }
  }

  render() {
    let source = this.state.imgURI ? { uri: this.state.imgURI } : null;
    if (!source && this.props.source) {
        source= this.props.source;
    }
    return (
      <View>
        {this.props.isBackground ? (
          <ImageBackground
            {...this.props}
            source={source}
          >
            {this.props.children}
          </ImageBackground>
        ) : (
          <Image
            {...this.props}
            source={source}
          />
        )}
      </View>
    );
  }
}
wagslane commented 4 years ago

@charles-goode Sorry could you provide context? There isn't even a diff here

charles-goode commented 4 years ago

If I understand the issue correctly, you want to be able to also pass app assets to this component perhaps as a local default for an image that may change to a remote URI later? I may have misread this. Do you mean instead to handle image sources retrieved from expo-imagepicker?

wagslane commented 4 years ago

Basically we want people to be able to use images that are already local (the same functionality as the RN Image component). The problem right now is that we are still caching those images for no reason and trying to download remote.

If you have a solution please open a PR and tag this issue