Is there a good library or maybe some default react native components that cache the image from a url?
I've tried react-native-cache-image but there are a lot of issues with react-native-fs and react-native-sqlite-storage and as I am new to react native I dont know how to fix them properly.
yarn add react-native-fast-image
Ref: https://github.com/DylanVann/react-native-fast-image
import FastImage from 'react-native-fast-image'
<FastImage
style={{ width: 200, height: 200 }}
source={{ uri: 'https://unsplash.it/400/400?image=1' }}
resizeMode={FastImage.resizeMode.stretch}
/>
You may be interested in my higher order component module that adds performance related image caching and "permanent cache" functionality to the native <Image> component. My module depends on react-native-fetch-blob which is the goto well-respected and battle-tested library for downloading files, so you shouldn't have dependency problems.
React Native Image Cache HOC
Tl;DR Code Example:
import imageCacheHoc from 'react-native-image-cache-hoc';
const CacheableImage = imageCacheHoc(Image);
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'}} permanent={true} />
</View>
);
}
}
The first image will be cached until the total local cache grows past 15 MB (by default) then cached images are deleted oldest first until total cache is below 15 MB again.
The second image will be stored to local disk permanently. People use this as a drop in replacement for shipping static image files with your app.
It also sounds like you are interested in arbitrarily storing image files to local disk. You can do that with a CacheableImage static method like so:
import imageCacheHoc from 'react-native-image-cache-hoc';
const CacheableImage = imageCacheHoc(Image);
CacheableImage.cacheFile('https://i.redd.it/hhhim0kc5swz.jpg', true)
.then( localFileInfo => {
console.log(localFileInfo);
// Console log outputs:
//{
// url: 'https://i.redd.it/rc29s4bz61uz.png',
// cacheType: 'permanent',
// localFilePath: '/this/is/absolute/path/to/file.jpg'
//}
});
Hope this helps!
Straight from the Expo docs:
import React from 'react';
import Expo from 'expo';
import { Image, View } from 'react-native';
import logo from './assets/icon.png';
const cacheImages = images => images.map(image => {
if (typeof image === 'string') return Image.prefetch(image);
return Expo.Asset.fromModule(image).downloadAsync();
});
class View extends React.component {
state = {
appIsReady: false
}
componentWillMount() {
this.loadAssetsAsync();
}
async loadAssetsAsync = () => {
const imageAssets = cacheImages([icon]);
await Promise.all([...imageAssets]);
this.setState({ appIsReady: true });
}
render() {
return(
<View>
<Image source={logo} style={styles.imageStyle} />
</View>
);
}
}
I think the code snippet is straight forward. You will have a state that will set to true when all your images are loaded. You will have the cacheImages function that will handle the work for you. The only requirement you will need is Expo.
I have used this library and working in both android and ios phones. It is working in Both EXPO and ReactNative. In react native automatically stored catch images.
For Installation the library:
yarn add picache
then use in your js file like this, first import the file and used it. For more information click
import Picache from "picache";
const App = () => (
<Picache
style={{ height: 150, width: 350 }}
source={require("./square.png")}
/>
);
There's prefetch() method built in Image component.
Related
I am building an app to show information of how covid is affected by different factors, like climate, and co2 level. So I used a webview to bring in a map from NASA. But when I navigate to that specific screen. The app crashes. It works fine sometimes, and crash mostly. My code is
import { StyleSheet, Text, View } from 'react-native'
import { WebView } from 'react-native-webview';
const GreenHouseScreen = () => {
return (
<View>
<WebView
style={{width: 600, height: 500}}
source={{uri: 'https://eodashboard.org/iframe?poi=W4-N2'}}
/>
</View>
)
}
export default GreenHouseScreen```
I have a very old react native application written on Expo using SDK version 32. Unfortunately, we don't have the time and resource to migrate to the latest version of the SDK (40).
So, right now, we struggle with an issue which reproduces on an Android. One of our screens has a background video (implemented with Expo Video component) and two buttons over it.
import React from 'react';
import { TouchableOpacity, StyleSheet, View, Text, Animated, Dimensions } from 'react-native';
import { AppLoading, Asset, Video } from 'expo';
const defaultVideoSource = require('../content/videos/be.mp4');
export default class HomeScreen extends React.Component {
static navigationOptions = {
header: null,
};
constructor(props) {
super(props);
this.state = {
backgroundOpacity: new Animated.Value(0),
loaded: false,
videoHeight: height,
videoWidth: width,
};
}
async componentWillMount() { }
// this is called from the video::onLoad()
fadeInVideo = () => {
const { backgroundOpacity } = this.state;
setTimeout(() => {
Animated.spring(backgroundOpacity, {
toValue: 1
}).start();
}, 400);
};
render() {
const { backgroundOpacity, loaded, videoHeight, videoWidth } = this.state;
if (!loaded) { return <AppLoading />; }
return (
<View style={styles.container}>
<View style={styles.background}>
<Animated.View
style={[
styles.backgroundViewWrapper,
{ opacity: backgroundOpacity }
]}
>
<Video
isLooping
isMuted={false}
onLoad={() => this.fadeInVideo()}
resizeMode="cover"
shouldPlay
source= { defaultVideoSource }
style={{ height: videoHeight, width: videoWidth }}
/>
</Animated.View>
</View>
<View style={styles.overlay}>
// BUTTONS OVER THE VIDEO
</View>
</View>
);
}
}
const styles = StyleSheet.create({
// ...
});
but for some reason, when I publish it to my Expo account and run it from there the video freezes at a certain moment and stops playing.
The buttons over the video are still active and functional. Like nothing happened but I need to restart the application (via Expo client app on Android) in order to start playing again. I have noticed that If I start it in a debug mode with the command:
expo start
and scan the QR code from the Expo client app there's no freezing of the video. So, for that reason, I would like to create a standalone file (.apk) which I can execute directly on my device without the need of using Expo client app. But, the problem here is that when I try to use the command:
expo build:android
I get an error saying:
Unsupported SDK version: our app builders no longer support SDK
version 32. Please, upgrade to at least SDK 36.
But, as I said, we currently don't have the time to do that because this (SDK update) would affect other components of our app that will need to be rewritten and retested. So, my question finally: Is there any alternative for building an .apk while using SDK 32.0?
It's possible to build apps that use old expo sdks, but I'm not sure if play store will accept those old versions, there was a lot of changes around privacy policy and terms of service plus at certain point google started to require 64 bit binaries(I'm not sure if sdk 32 already had those changes). If that's the case your only option is to upgrade to supported SDK.
If you want to build that app, you can do that with turtle-cli https://www.npmjs.com/package/turtle-cli. This tools executes locally the same code that runs on expo servers, but it's not fully integrated with expo infrastructure, so you will need to provide keystore and all the passwords manually in the cli and either your expo credentials(if you use expo publish) or url to the js bundle (if you self-host). You will need to use old enough version of turtle-cli that still have that sdk, you can consult CHANGELOG here https://github.com/expo/turtle/blob/master/CHANGELOG.md to check that.
As the question says my Webview component wont load its content. I have no errors poping up or any problem in the conosle related to WebView. I am using expo.
On the other hand when using Iframe I have no problem loading the content running test on web browser but wont work when running test on mobile device. I have tried every solution proposed by others in here but nothing seems to work. I even tried using the iframe inside the WebView using html instead of uri, also nesting the WebView component into a View and giving it flex:1 but still nothing comes up on the screen but a small red dot up in the right corner.
As you can see all the test I've done with the commented code. The first (commented) function is using the Iframe component and the second (not commented) is only WebView.
My code looks like this:
import React from 'react';
import { StyleSheet, Text, Button, View, Dimensions } from 'react-native';
import Iframe from 'react-iframe';
import Header from './Header';
import ButtonMenu from './ButtonMenu';
import { WebView } from 'react-native-webview';
const deviceHeight = Dimensions.get('window').height;
const deviceWidth = Dimensions.get('window').width;
// export default function NatureQuestGps({navigation}) {
// return (
// <View style={styles.pageNatureQuest}>
// <View style={styles.headerContainer}>
// <Header/>
// <ButtonMenu color='#49AC72' onPress={()=> navigation.toggleDrawer()} />
// </View>
// <Iframe url="https://www.google.com/maps/d/embed?mid=11fEhEZiv72kKKy7XbbQ3GIufEcTpWUa2"
// width="400px"
// height="400px"
// display="initial"
// position="relative"/>
// </View>
// );
// }
export default function NatureQuestGps() {
return (
<View style={{ flex: 1, alignItems: 'flex-end' }}>
<WebView
source= {{ uri: 'https://www.google.com/maps/d/embed?mid=11fEhEZiv72kKKy7XbbQ3GIufEcTpWUa2'}}
style={styles.webview}
javaScriptEnabled={true}
domStorageEnabled={true}
startInLoadingState={false}
scalesPageToFit={true}
/>
</View>
);
}
const styles = StyleSheet.create({
pageNatureQuest:{
flex:1,
flexDirection:'column',
alignItems:'center',
},
headerContainer: {
width:'100%',
backgroundColor:'#ededed',
},
webview: {
flex: 1,
backgroundColor: 'yellow',
width: deviceWidth,
height: deviceHeight
}
});
Thanks in advance for anyone taking the time to help
I found the solution and it turns out that WebView component works when testing in mobile with EXPO but naver loaded in my browser, and the Iframe that worked in my browser but was not working in my mobile. Anyways the answer is that if you want a google maps trail or parcour to work for mobile and also web you will need a library to create your own maps routes and trails. But if you are only making a mobile app go for the WebView component although it possibly wont work in your computer browser for tests.
Sorry in advance if this is a silly question but I'm going in circles here. I'm pretty new to react and node, and looking to connect my react native front end with my node back end. Pretty much every tutorial I read is telling me to "paste the fetch api code after the class declaration and before the render method", but I don't have a class declaration or render method in any of my code, so I'm a little confused about where to put the fetch api code? I don't know if this makes a huge difference but each screen of my app is in it's own file as it's own function and I am using a stack navigator to get between the screens with it's functionality contained in another file for the stack navigator.
If anyone could offer me some help I would really appreciate it, I am beyond confused about what to do.
Included below is my Login Screen function so you can see what I mean when I say the screens are set up as functions. All other screens are laid out the same way. Please let me know if you need to see anything else and I will edit and add it in!
import React from 'react';
function LoginScreen() {
return(
<View style={{ alignItems: "center", marginTop: 24 }}>
<Text style={styles.text}>
Login Screen
</Text>
</View>
);
}
export default LoginScreen
Again, I would appreciate any help anyone could give me as this is driving me demented and I would really like to get this working!
With functional components, you declare your "methods" inside your function:
function LoginScreen() {
const submit = () => {
//do something
}
return <View>
<Button title="Submit" onPress={submit} />
</View>
}
So, You are trying to use Functional Components instead of Class Based Component.
In Functional Components to send FetchAPI Request we use React Hooks, useEffect to send api request, and save the return data of API Request in another hook useState.
Please read more about React Hooks here,
https://reactjs.org/docs/hooks-intro.html
In your above scenario fetch api request with functional components will be something like this,
import React, {useEffect, useState} from 'react';
function LoginScreen() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('yourapi.com/user')
.then(res => res.json())
.then(data => setData(data));
}, []);
return(
<View style={{ alignItems: "center", marginTop: 24 }}>
<Text style={styles.text}>
// You Can Use Your Data Here {data}
Login Screen
</Text>
</View>
);
}
export default LoginScreen
A most pro and less verbose way to do it is using axios library. Check this code out:
import React, { useState, useEffect } from 'react';
import { View, Text } from 'react-native';
import axios from 'axios';
export default function LoginScreen() {
const [user, setUser] = useState({});
const termToSearchFor = 'edilsonlm217';
useEffect(() => {
async function loadAPI() {
const response = await axios.get(`https://api.github.com/users/${termToSearchFor}`);
setUser(response.data);
}
loadAPI();
}, []);
return (
<View style={{ alignItems: "center", marginTop: 24 }}>
<Text>
{user.bio}
</Text>
</View>
);
}
This piece of code searchs for my user "edilsonlm217" in the public github's API right away after the component load. It gets the response from response.data and set it to user state then print my bio on screen.
Don't forget to install axios as dependecy to your project before use it.
I am wanting to develop an app where a user can select different gamepacks to install on their android or ios device. A gamepack will consist of:
one or more JSON files
images
sounds
Right now I'm not really concerned if these are transferred individually or in a zip file (though a zip file would certainly be preferred). Of course the device will need to be connected to the Internet to get the current list of gamepacks and to download the ones that the user chooses. Once the gamepacks are downloaded to the phone the user will not need an Internet connection to play the game as they will have all the files.
How do I go about implementing this in my project?
How do I download the files? Would I use the react-native-fetch-blob library and save it in a specific location? This library refers to saving it as "cache" rather than permanently, so I am not sure if this is the correct solution. The specific section I am looking at on the library page is "Use Specific File Path". But because it is cache, should I be looking for something else that is more of a longer term storage? It does say on the page that files will not be deleted so I am a bit confused as to what the difference between permanent storage and cache is in this case.
Once the files are downloaded would I then be able to open images and display them, open sound files and play them, and open the json files and process them?
Check out React Native FS, specifically the documentation on downloadFile:
https://github.com/johanneslumpe/react-native-fs#downloadfileoptions-downloadfileoptions--jobid-number-promise-promisedownloadresult-
Here's a working example:
import React, { Component } from 'react';
import {
AppRegistry,
Text,
View,
Image,
} from 'react-native';
import RNFS from 'react-native-fs';
export default class downloadFile extends Component {
constructor() {
super()
this.state = {
isDone: false,
};
this.onDownloadImagePress = this.onDownloadImagePress.bind(this);
}
onDownloadImagePress() {
RNFS.downloadFile({
fromUrl: 'https://facebook.github.io/react-native/img/header_logo.png',
toFile: `${RNFS.DocumentDirectoryPath}/react-native.png`,
}).promise.then((r) => {
this.setState({ isDone: true })
});
}
render() {
const preview = this.state.isDone ? (<View>
<Image style={{
width: 100,
height: 100,
backgroundColor: 'black',
}}
source={{
uri: `file://${RNFS.DocumentDirectoryPath}/react-native.png`,
scale: 1
}}
/>
<Text>{`file://${RNFS.DocumentDirectoryPath}/react-native.png`}</Text>
</View>
) : null;
return (
<View>
<Text onPress={this.onDownloadImagePress}>Download Image</Text>
{preview}
</View>
);
}
}
AppRegistry.registerComponent('downloadFile', () => downloadFile);
It's important to know that the height and width must be set on the Image